From fc388133a638d4288314dd3faeddfcce61fe7292 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 10:24:37 +0800 Subject: [PATCH 001/595] Fix: event Deskripsi: - Create event konfirmasi --- prisma/schema.prisma | 1 + src/app/api/origin-url/route.ts | 6 + src/app/dev/event/konfirmasi/[id]/page.tsx | 40 ++++++ src/app/dev/home/page.tsx | 6 +- .../component/comp_avatar_and_username.tsx | 2 +- .../event/table_status/table_publish.tsx | 21 ++- src/app_modules/event/_ui/konfirmasi.tsx | 136 ++++++++++++++++++ .../event/component/detail/list_peserta.tsx | 126 +++++++++++++++- .../edit/fun_update_konfirmasi_by_user_id.ts | 24 ++++ .../event/fun/get/fun_check_kehadiran.ts | 27 ++++ .../fun/get/fun_check_peserta_by_user_id.ts | 24 ++++ .../event/fun/get/get_list_peserta_by_id.ts | 4 +- src/app_modules/event/fun/index.ts | 6 + src/app_modules/event/model/interface.ts | 1 + src/app_modules/home/view_home.tsx | 11 ++ src/app_modules/job/detail/main/view.tsx | 4 +- src/middleware.ts | 1 + 17 files changed, 429 insertions(+), 11 deletions(-) create mode 100644 src/app/api/origin-url/route.ts create mode 100644 src/app/dev/event/konfirmasi/[id]/page.tsx create mode 100644 src/app_modules/event/_ui/konfirmasi.tsx create mode 100644 src/app_modules/event/fun/edit/fun_update_konfirmasi_by_user_id.ts create mode 100644 src/app_modules/event/fun/get/fun_check_kehadiran.ts create mode 100644 src/app_modules/event/fun/get/fun_check_peserta_by_user_id.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index faebf9e8..bda6bd67 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -618,6 +618,7 @@ model Event_Peserta { active Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + isPresent Boolean @default(false) Event Event? @relation(fields: [eventId], references: [id]) eventId String? diff --git a/src/app/api/origin-url/route.ts b/src/app/api/origin-url/route.ts new file mode 100644 index 00000000..186624ae --- /dev/null +++ b/src/app/api/origin-url/route.ts @@ -0,0 +1,6 @@ +import { headers } from "next/headers"; +export async function GET(req: Request) { + const origin = new URL(req.url).origin; + + return new Response(JSON.stringify({ success: true, origin })); +} diff --git a/src/app/dev/event/konfirmasi/[id]/page.tsx b/src/app/dev/event/konfirmasi/[id]/page.tsx new file mode 100644 index 00000000..a7de2574 --- /dev/null +++ b/src/app/dev/event/konfirmasi/[id]/page.tsx @@ -0,0 +1,40 @@ +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import Ui_Konfirmasi from "@/app_modules/event/_ui/konfirmasi"; +import { + event_funCheckKehadiran, + event_funCheckPesertaByUserId, +} from "@/app_modules/event/fun"; +import { event_getOneById } from "@/app_modules/event/fun/get/get_one_by_id"; +import { redirect } from "next/navigation"; + +export default async function Page({ params }: { params: { id: string } }) { + const eventId = params.id; + const userLoginId = await funGetUserIdByToken(); + const dataEvent = await event_getOneById(eventId); + + const checkPeserta = await event_funCheckPesertaByUserId({ + eventId: eventId, + userId: userLoginId as string, + }); + + + + if (dataEvent?.isArsip) + return redirect(`/dev/event/detail/riwayat/${dataEvent.id}`); + + if (checkPeserta == false) + return redirect(`/dev/event/detail/main/${eventId}`); + + // if (checkKehadiran) { + // return redirect(`/dev/event/main/beranda`); + // } + + return ( + <> + + + ); +} diff --git a/src/app/dev/home/page.tsx b/src/app/dev/home/page.tsx index 9e8826a8..e9a9c33d 100644 --- a/src/app/dev/home/page.tsx +++ b/src/app/dev/home/page.tsx @@ -4,8 +4,6 @@ import { HomeView } from "@/app_modules/home"; import { user_getOneByUserId } from "@/app_modules/home/fun/get/get_one_user_by_id"; import { job_getTwoForHomeView } from "@/app_modules/job/fun/get/get_two_for_home_view"; import notifikasi_countUserNotifikasi from "@/app_modules/notifikasi/fun/count/fun_count_by_id"; - -import { cookies } from "next/headers"; import { redirect } from "next/navigation"; export default async function PageHome() { @@ -20,8 +18,8 @@ export default async function PageHome() { // if (dataUser?.active === false) { // return redirect(RouterHome.home_user_non_active); // } - if (dataUser?.masterUserRoleId === "2" || dataUser?.masterUserRoleId === "3") - return redirect(RouterAdminDashboard.main_admin); + // if (dataUser?.masterUserRoleId === "2" || dataUser?.masterUserRoleId === "3") + // return redirect(RouterAdminDashboard.main_admin); return ( <> diff --git a/src/app_modules/_global/component/comp_avatar_and_username.tsx b/src/app_modules/_global/component/comp_avatar_and_username.tsx index 9269be4a..d8e419b9 100644 --- a/src/app_modules/_global/component/comp_avatar_and_username.tsx +++ b/src/app_modules/_global/component/comp_avatar_and_username.tsx @@ -51,7 +51,7 @@ export function ComponentGlobal_AvatarAndUsername({ ) : ( )} diff --git a/src/app_modules/admin/event/table_status/table_publish.tsx b/src/app_modules/admin/event/table_status/table_publish.tsx index 10ec7cd1..7a5ae296 100644 --- a/src/app_modules/admin/event/table_status/table_publish.tsx +++ b/src/app_modules/admin/event/table_status/table_publish.tsx @@ -24,6 +24,7 @@ import { useState } from "react"; import ComponentAdminGlobal_HeaderTamplate from "../../_admin_global/header_tamplate"; import { adminEvent_funGetListPublish } from "../fun"; import QRCode from "react-qr-code"; +import { useShallowEffect } from "@mantine/hooks"; export default function AdminEvent_TablePublish({ listPublish, @@ -49,6 +50,21 @@ function TableStatus({ listPublish }: { listPublish: any }) { const [eventId, setEventId] = useState(""); const [loading, setLoading] = useState(false); + const [origin, setOrigin] = useState(""); + + useShallowEffect(() => { + onLoadOrigin(setOrigin); + // if (typeof window !== "undefined") { + // setOrigin(window.location.origin); + // } + }, [setOrigin]); + + async function onLoadOrigin(setOrigin: any) { + const res = await fetch("/api/origin-url"); + const result = await res.json(); + setOrigin(result.origin); + } + async function onSearch(s: string) { setSearch(s); const loadData = await adminEvent_funGetListPublish({ @@ -80,7 +96,10 @@ function TableStatus({ listPublish }: { listPublish: any }) {
- +
diff --git a/src/app_modules/event/_ui/konfirmasi.tsx b/src/app_modules/event/_ui/konfirmasi.tsx new file mode 100644 index 00000000..468e8c14 --- /dev/null +++ b/src/app_modules/event/_ui/konfirmasi.tsx @@ -0,0 +1,136 @@ +"use client"; + +import { + UIGlobal_LayoutDefault, + UIGlobal_LayoutTamplate, +} from "@/app_modules/_global/ui"; +import { Button, Paper, Skeleton, Stack, Text, Title } from "@mantine/core"; +import { MODEL_EVENT } from "../model/interface"; +import { useShallowEffect } from "@mantine/hooks"; +import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import { event_funCheckKehadiran, event_funUpdateKehadiran } from "../fun"; +import { + ComponentGlobal_NotifikasiBerhasil, + ComponentGlobal_NotifikasiGagal, +} from "@/app_modules/_global/notif_global"; +import { useRouter } from "next/navigation"; +import { RouterEvent } from "@/app/lib/router_hipmi/router_event"; +import { useState } from "react"; + +export default function Ui_Konfirmasi({ + dataEvent, + userLoginId, +}: { + dataEvent: MODEL_EVENT; + userLoginId: string; +}) { + // console.log(dataEvent); + + const router = useRouter(); + const [isLoading, setLoading] = useState(false); + const [isPresent, setIsPresent] = useState(null); + + // useShallowEffect(() => { + // onLoadData({ + // onPublish(val) { + // setData(val); + // }, + // }); + // }, [setData]); + + useShallowEffect(() => { + onLoadKehadiran({ + onChange(val) { + setIsPresent(val); + }, + }); + }, [setIsPresent]); + + async function onLoadKehadiran({ + onChange, + }: { + onChange: (val: boolean) => void; + }) { + const checkKehadiran = await event_funCheckKehadiran({ + eventId: dataEvent.id, + userId: userLoginId as string, + }); + + onChange(checkKehadiran); + } + async function onUpdateKonfirmasi() { + setLoading(true); + const res = await event_funUpdateKehadiran({ + eventId: dataEvent.id, + userId: userLoginId, + }); + + if (res.status === 200) { + ComponentGlobal_NotifikasiBerhasil(res.message, 2000); + router.push(RouterEvent.detail_main + dataEvent.id); + } else { + setLoading(false); + ComponentGlobal_NotifikasiGagal(res.message); + } + } + + if (isPresent === null) { + return <>; + } + + return ( + <> + + + {isPresent == null ? ( + + ) : isPresent ? ( + + + + Anda telah terkonfirmasi silahkan kembali ke beranda ! + + {dataEvent.title} + + + + ) : ( + + + + Anda mengkonfirmasi bahwa anda telah datang & ikut menghadir + di event + + {dataEvent.title} + + + + )} + + + + ); +} diff --git a/src/app_modules/event/component/detail/list_peserta.tsx b/src/app_modules/event/component/detail/list_peserta.tsx index 9ae7274b..73d56a58 100644 --- a/src/app_modules/event/component/detail/list_peserta.tsx +++ b/src/app_modules/event/component/detail/list_peserta.tsx @@ -1,19 +1,31 @@ "use client"; import { + ActionIcon, + Avatar, Center, + Grid, + Group, Stack, Text, - Title + Title, } from "@mantine/core"; import _ from "lodash"; import { ComponentGlobal_AvatarAndUsername, ComponentGlobal_CardStyles, + ComponentGlobal_LoaderAvatar, } from "@/app_modules/_global/component"; import { useRouter } from "next/navigation"; import { MODEL_EVENT_PESERTA } from "../../model/interface"; +import { Prisma } from "@prisma/client"; +import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; +import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; +import { funGlobal_CheckProfile } from "@/app_modules/_global/fun/get"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { useState } from "react"; +import moment from "moment"; export default function ComponentEvent_ListPeserta({ listPeserta, @@ -41,11 +53,21 @@ export default function ComponentEvent_ListPeserta({ {listPeserta.map((e, i) => ( - */} + + {/* */} ))} @@ -56,3 +78,101 @@ export default function ComponentEvent_ListPeserta({ ); } + +type IFontSize = "xs" | "sm" | "md" | "lg" | "xl"; + +function ComponentEvent_AvatarAndUsername({ + profile, + component, + sizeAvatar, + fontSize, + tanggalMulai, + tanggalSelesai, + isPresent, +}: { + profile: Prisma.ProfileSelect; + component?: React.ReactNode; + sizeAvatar?: number; + fontSize?: IFontSize | {}; + tanggalMulai?: Date; + tanggalSelesai?: Date; + isPresent?: boolean; +}) { + const router = useRouter(); + const [visible, setVisible] = useState(false); + + async function onCheckProfile() { + const res = await funGlobal_CheckProfile({ profileId: profile.id as any }); + + if (res !== null) { + setVisible(true); + router.push(RouterProfile.katalog({ id: profile.id as any })); + } else { + ComponentGlobal_NotifikasiPeringatan("Id tidak ditemukan"); + } + } + + const tglMulai = moment(tanggalMulai).diff(moment(), "minutes"); + + const tglSelesai = moment(tanggalSelesai).diff(moment(), "minutes"); + + // console.log("mulai:", tglMulai, "selesai:", tglSelesai); + + return ( + <> + + + onCheckProfile()} + > + {visible ? ( + + + + ) : ( + + )} + + + + + + onCheckProfile()} + > + {profile?.name} + + + + + {/* {component && ( + + + {component} + + + )} */} + + {tglMulai < 0 && ( + + + + + {isPresent ? "Hadir" : "-"} + + + + + )} + + + ); +} diff --git a/src/app_modules/event/fun/edit/fun_update_konfirmasi_by_user_id.ts b/src/app_modules/event/fun/edit/fun_update_konfirmasi_by_user_id.ts new file mode 100644 index 00000000..5799550b --- /dev/null +++ b/src/app_modules/event/fun/edit/fun_update_konfirmasi_by_user_id.ts @@ -0,0 +1,24 @@ +"use server"; + +import { prisma } from "@/app/lib"; + +export async function event_funUpdateKehadiran({ + userId, + eventId, +}: { + userId: string; + eventId: string; +}) { + const updt = await prisma.event_Peserta.updateMany({ + where: { + userId: userId, + eventId: eventId, + }, + data: { + isPresent: true, + }, + }); + + if(!updt) return { status: 400, message: "Gagal Update" }; + return { status: 200, message: "Anda telah terkonfirmasi" }; +} diff --git a/src/app_modules/event/fun/get/fun_check_kehadiran.ts b/src/app_modules/event/fun/get/fun_check_kehadiran.ts new file mode 100644 index 00000000..4bb9cc84 --- /dev/null +++ b/src/app_modules/event/fun/get/fun_check_kehadiran.ts @@ -0,0 +1,27 @@ +"use server"; + +import { prisma } from "@/app/lib"; + +export async function event_funCheckKehadiran({ + userId, + eventId, +}: { + userId: string; + eventId: string; +}) { + const checkKehadiran = await prisma.event_Peserta.findFirst({ + where: { + userId: userId, + eventId: eventId, + }, + select: { + isPresent: true, + }, + }); + + if (checkKehadiran?.isPresent) { + return true; + } else { + return false; + } +} diff --git a/src/app_modules/event/fun/get/fun_check_peserta_by_user_id.ts b/src/app_modules/event/fun/get/fun_check_peserta_by_user_id.ts new file mode 100644 index 00000000..ba369915 --- /dev/null +++ b/src/app_modules/event/fun/get/fun_check_peserta_by_user_id.ts @@ -0,0 +1,24 @@ +"use server"; + +import { prisma } from "@/app/lib"; + +export async function event_funCheckPesertaByUserId({ + userId, + eventId, +}: { + userId: string; + eventId: string; +}) { + const check = await prisma.event_Peserta.findFirst({ + where: { + userId: userId, + eventId: eventId, + }, + }); + + if (check != null) { + return true; + } else { + return false; + } +} diff --git a/src/app_modules/event/fun/get/get_list_peserta_by_id.ts b/src/app_modules/event/fun/get/get_list_peserta_by_id.ts index 1b490246..148029a7 100644 --- a/src/app_modules/event/fun/get/get_list_peserta_by_id.ts +++ b/src/app_modules/event/fun/get/get_list_peserta_by_id.ts @@ -13,12 +13,14 @@ export async function Event_getListPesertaById(eventId: string) { createdAt: true, updatedAt: true, userId: true, + + isPresent: true, User: { select: { Profile: true, }, }, - // Event: true, + Event: true, eventId: true, }, }); diff --git a/src/app_modules/event/fun/index.ts b/src/app_modules/event/fun/index.ts index e8dffb0d..39702477 100644 --- a/src/app_modules/event/fun/index.ts +++ b/src/app_modules/event/fun/index.ts @@ -1,5 +1,11 @@ +import { event_funUpdateKehadiran } from "./edit/fun_update_konfirmasi_by_user_id"; +import { event_funCheckKehadiran } from "./get/fun_check_kehadiran"; +import { event_funCheckPesertaByUserId } from "./get/fun_check_peserta_by_user_id"; import { event_getAllByStatusId } from "./get/status/get_all_by_status_id"; import { event_getMasterStatus } from "./master/get_status_event"; export { event_getAllByStatusId }; export { event_getMasterStatus }; +export { event_funCheckPesertaByUserId }; +export { event_funUpdateKehadiran }; +export { event_funCheckKehadiran }; diff --git a/src/app_modules/event/model/interface.ts b/src/app_modules/event/model/interface.ts index 24b5ac96..27a0d3f9 100644 --- a/src/app_modules/event/model/interface.ts +++ b/src/app_modules/event/model/interface.ts @@ -30,5 +30,6 @@ export interface MODEL_EVENT_PESERTA { userId: string; User: MODEL_USER; eventId: string; + isPresent: boolean Event: MODEL_EVENT; } diff --git a/src/app_modules/home/view_home.tsx b/src/app_modules/home/view_home.tsx index 01d0052d..403657f0 100644 --- a/src/app_modules/home/view_home.tsx +++ b/src/app_modules/home/view_home.tsx @@ -15,6 +15,7 @@ import { gs_count_ntf, gs_user_ntf } from "@/app/lib/global_state"; import { useAtom } from "jotai"; import notifikasi_countUserNotifikasi from "../notifikasi/fun/count/fun_count_by_id"; import { Center, Text, Title } from "@mantine/core"; +import { useRouter } from "next/navigation"; export default function HomeView({ dataUser, @@ -25,9 +26,19 @@ export default function HomeView({ dataJob: MODEL_JOB[]; countNotifikasi: number; }) { + const router = useRouter(); const [countNtf, setCountNtf] = useState(countNotifikasi); const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); + const userRoleId = dataUser.masterUserRoleId; + + useShallowEffect(() => { + if (userRoleId === "2" || userRoleId === "3") { + setTimeout(() => { + router.push("/waiting-room", { scroll: false }); + }, 1000); + } + }, [userRoleId]); useShallowEffect(() => { onLoadNotifikasi({ diff --git a/src/app_modules/job/detail/main/view.tsx b/src/app_modules/job/detail/main/view.tsx index c159fc61..a9e21aec 100644 --- a/src/app_modules/job/detail/main/view.tsx +++ b/src/app_modules/job/detail/main/view.tsx @@ -28,11 +28,13 @@ function ButtonAction({ jobId }: { jobId: string }) { // if (typeof window !== "undefined") { // setOrigin(window.location.origin); // } + }, [setOrigin]); async function onLoadOrigin(setOrigin: any) { - const res = await fetch("/api/zz-makuro"); + const res = await fetch("/api/origin-url"); const result = await res.json(); + console.log(result); setOrigin(result.origin); } diff --git a/src/middleware.ts b/src/middleware.ts index c0c3f708..30cee1e5 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -20,6 +20,7 @@ const middlewareConfig: MiddlewareConfig = { publicRoutes: [ "/", "/api/auth/*", + "/api/origin-url", "/login", "/register", "/validasi", From 9338ebe3d73d6c114f904e679f5ec1967ae1fe98 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 10:26:12 +0800 Subject: [PATCH 002/595] chore(release): 1.2.13 --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0a26813..3db3fc35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ 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.13](https://github.com/bipproduction/hipmi/compare/v1.2.12...v1.2.13) (2024-12-03) + + +### Bug Fixes + +* event ([fc38813](https://github.com/bipproduction/hipmi/commit/fc388133a638d4288314dd3faeddfcce61fe7292)) +* version 1.2.12 ([932735f](https://github.com/bipproduction/hipmi/commit/932735f9238db07b331b1b26328624247371c3d6)) + ## [1.2.12](https://github.com/bipproduction/hipmi/compare/v1.2.11...v1.2.12) (2024-12-02) diff --git a/package.json b/package.json index 3b1dba92..6c3f6157 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.12", + "version": "1.2.13", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 11230dbf01eacdb294673ed7e0536a9b85d88579 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 10:26:56 +0800 Subject: [PATCH 003/595] Fix version --- src/app_modules/admin/event/table_status/table_publish.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app_modules/admin/event/table_status/table_publish.tsx b/src/app_modules/admin/event/table_status/table_publish.tsx index 7a5ae296..01316545 100644 --- a/src/app_modules/admin/event/table_status/table_publish.tsx +++ b/src/app_modules/admin/event/table_status/table_publish.tsx @@ -101,7 +101,7 @@ function TableStatus({ listPublish }: { listPublish: any }) { value={`${origin}/dev/event/konfirmasi/${e.id}`} /> - +
{e?.Author?.username} From f6b990fccd01c66b8fe6f604dbc7472e6cd53096 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 10:55:30 +0800 Subject: [PATCH 004/595] chore(release): 1.2.14 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3db3fc35..9021a9a8 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.14](https://github.com/bipproduction/hipmi/compare/v1.2.13...v1.2.14) (2024-12-03) + ## [1.2.13](https://github.com/bipproduction/hipmi/compare/v1.2.12...v1.2.13) (2024-12-03) diff --git a/package.json b/package.json index 6c3f6157..a2e48c00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.13", + "version": "1.2.14", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From bd728c07d6828c6cded4b3dc86b406f6f9ad3aa2 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 10:56:23 +0800 Subject: [PATCH 005/595] Fix version 1.2.13 --- src/app/dev/home/page.tsx | 5 +++-- src/bin/seeder/user_seeder.json | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/dev/home/page.tsx b/src/app/dev/home/page.tsx index e9a9c33d..91fb6632 100644 --- a/src/app/dev/home/page.tsx +++ b/src/app/dev/home/page.tsx @@ -18,8 +18,9 @@ export default async function PageHome() { // if (dataUser?.active === false) { // return redirect(RouterHome.home_user_non_active); // } - // if (dataUser?.masterUserRoleId === "2" || dataUser?.masterUserRoleId === "3") - // return redirect(RouterAdminDashboard.main_admin); + + if (dataUser?.masterUserRoleId === "2" || dataUser?.masterUserRoleId === "3") + return redirect(RouterAdminDashboard.main_admin); return ( <> diff --git a/src/bin/seeder/user_seeder.json b/src/bin/seeder/user_seeder.json index afefe855..ce89edb2 100644 --- a/src/bin/seeder/user_seeder.json +++ b/src/bin/seeder/user_seeder.json @@ -2,7 +2,7 @@ { "name": "bagas_admin", "nomor": "6282340374412", - "masterUserRoleId": "2" + "masterUserRoleId": "3" }, { "name": "fahmi_admin", From 304476b3672fc720d6f480ec4af931d04f982a00 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 11:59:01 +0800 Subject: [PATCH 006/595] Fix seeder --- src/app_modules/_global/fun/generate_seeder.ts | 2 ++ src/bin/seeder/user_seeder.json | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app_modules/_global/fun/generate_seeder.ts b/src/app_modules/_global/fun/generate_seeder.ts index c1f89d11..988da404 100644 --- a/src/app_modules/_global/fun/generate_seeder.ts +++ b/src/app_modules/_global/fun/generate_seeder.ts @@ -52,11 +52,13 @@ export async function generate_seeder() { nomor: i.nomor, username: i.name, masterUserRoleId: i.masterUserRoleId, + active: i.active, }, update: { nomor: i.nomor, username: i.name, masterUserRoleId: i.masterUserRoleId, + active: i.active, }, }); } diff --git a/src/bin/seeder/user_seeder.json b/src/bin/seeder/user_seeder.json index ce89edb2..356be9bf 100644 --- a/src/bin/seeder/user_seeder.json +++ b/src/bin/seeder/user_seeder.json @@ -2,11 +2,13 @@ { "name": "bagas_admin", "nomor": "6282340374412", - "masterUserRoleId": "3" + "masterUserRoleId": "3", + "active": true }, { "name": "fahmi_admin", "nomor": "628123833845", - "masterUserRoleId": "2" + "masterUserRoleId": "2", + "active": true } ] From dc69a67a8410f30ac15d8d9e4064352cbc96398e Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 13:32:25 +0800 Subject: [PATCH 007/595] Fix redirect --- src/app/api/auth/register/route.ts | 1 + src/app/dev/admin/layout.tsx | 11 ++++++++--- src/app/dev/home/page.tsx | 6 ++---- src/app/dev/layout.tsx | 16 +++++++++++----- src/app_modules/auth/register/view.tsx | 2 +- src/app_modules/auth/validasi/view.tsx | 14 +++++++------- src/app_modules/home/view_home.tsx | 14 +++++++------- 7 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts index 8410af4b..19366387 100644 --- a/src/app/api/auth/register/route.ts +++ b/src/app/api/auth/register/route.ts @@ -24,6 +24,7 @@ export async function POST(req: Request) { data: { username: data.username, nomor: data.nomor, + active: true, }, }); diff --git a/src/app/dev/admin/layout.tsx b/src/app/dev/admin/layout.tsx index 212636a4..b267ac9e 100644 --- a/src/app/dev/admin/layout.tsx +++ b/src/app/dev/admin/layout.tsx @@ -5,8 +5,9 @@ import adminNotifikasi_countNotifikasi from "@/app_modules/admin/notifikasi/fun/ import adminNotifikasi_getByUserId from "@/app_modules/admin/notifikasi/fun/get/get_notifikasi_by_user_id"; import React from "react"; import versionUpdate from "../../../../package.json"; +import { redirect } from "next/navigation"; -export default async function Layout({ +export default async function Layout({ children, }: { children: React.ReactNode; @@ -14,10 +15,14 @@ export default async function Layout({ const userLoginId = await funGetUserIdByToken(); const version = versionUpdate.version; - const dataUser = await funGlobal_getUserById({ userId: userLoginId as string}); - const listNotifikasi = await adminNotifikasi_getByUserId({page: 1}); + const dataUser = await funGlobal_getUserById({ + userId: userLoginId as string, + }); + const listNotifikasi = await adminNotifikasi_getByUserId({ page: 1 }); const countNotifikasi = await adminNotifikasi_countNotifikasi(); + if (dataUser?.masterUserRoleId == "1") return redirect("/dev/home"); + return ( <> diff --git a/src/app/dev/layout.tsx b/src/app/dev/layout.tsx index 0ea9defe..4c91bd10 100644 --- a/src/app/dev/layout.tsx +++ b/src/app/dev/layout.tsx @@ -12,12 +12,18 @@ export default async function Layout({ children: React.ReactNode; }) { const userLoginId = await funGetUserIdByToken(); - const activationUser = await funGlobal_checkActivationUseById({ - userId: userLoginId as string, - }); + // const dataUser = await funGlobal_getUserById({ + // userId: userLoginId as string, + // }); + + // if (dataUser?.masterUserRoleId != "1") return redirect("/dev/home"); + + // const activationUser = await funGlobal_checkActivationUseById({ + // userId: userLoginId as string, + // }); + + // if (activationUser == false) return redirect("/waiting-room"); - if (activationUser == false) return redirect("/waiting-room"); - return ( <> { - if (userRoleId === "2" || userRoleId === "3") { - setTimeout(() => { - router.push("/waiting-room", { scroll: false }); - }, 1000); - } - }, [userRoleId]); + // useShallowEffect(() => { + // if (userRoleId === "2" || userRoleId === "3") { + // setTimeout(() => { + // router.push("/waiting-room", { scroll: false }); + // }, 1000); + // } + // }, [userRoleId]); useShallowEffect(() => { onLoadNotifikasi({ From 2bf6864f528d5a50213f798c4ce7dc51e78b2805 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 14:00:29 +0800 Subject: [PATCH 008/595] chore(release): 1.2.15 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9021a9a8..2be83116 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.15](https://github.com/bipproduction/hipmi/compare/v1.2.14...v1.2.15) (2024-12-03) + ## [1.2.14](https://github.com/bipproduction/hipmi/compare/v1.2.13...v1.2.14) (2024-12-03) ## [1.2.13](https://github.com/bipproduction/hipmi/compare/v1.2.12...v1.2.13) (2024-12-03) diff --git a/package.json b/package.json index a2e48c00..d3b98567 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.14", + "version": "1.2.15", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 27027d036d4073afe9d92120d529ed38dca1a147 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 14:00:58 +0800 Subject: [PATCH 009/595] Fix version 1.2.15 --- src/app/dev/{ => (user)}/home/page.tsx | 0 src/app/dev/(user)/layout.tsx | 20 +++++++++ .../fun/get/fun_get_user_id_by_token.ts | 42 +++++++++++++++---- 3 files changed, 54 insertions(+), 8 deletions(-) rename src/app/dev/{ => (user)}/home/page.tsx (100%) create mode 100644 src/app/dev/(user)/layout.tsx diff --git a/src/app/dev/home/page.tsx b/src/app/dev/(user)/home/page.tsx similarity index 100% rename from src/app/dev/home/page.tsx rename to src/app/dev/(user)/home/page.tsx diff --git a/src/app/dev/(user)/layout.tsx b/src/app/dev/(user)/layout.tsx new file mode 100644 index 00000000..0017c962 --- /dev/null +++ b/src/app/dev/(user)/layout.tsx @@ -0,0 +1,20 @@ +import { RouterAdminDashboard } from "@/app/lib/router_hipmi/router_admin"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import { funGlobal_getUserById } from "@/app_modules/_global/fun/get/fun_get_user_by_id"; +import { redirect } from "next/navigation"; + +export default async function Layout({ + children, +}: { + children: React.ReactNode; +}) { + const userLoginId = await funGetUserIdByToken(); + const dataUser = await funGlobal_getUserById({ + userId: userLoginId as string, + }); + + if (dataUser?.masterUserRoleId != "1") + return redirect(RouterAdminDashboard.splash_admin); + + return <>{children}; +} diff --git a/src/app_modules/_global/fun/get/fun_get_user_id_by_token.ts b/src/app_modules/_global/fun/get/fun_get_user_id_by_token.ts index 22249f85..d08bc9cb 100644 --- a/src/app_modules/_global/fun/get/fun_get_user_id_by_token.ts +++ b/src/app_modules/_global/fun/get/fun_get_user_id_by_token.ts @@ -3,10 +3,17 @@ import { prisma } from "@/app/lib"; import { ServerEnv } from "@/app/lib/server_env"; import { unsealData } from "iron-session"; +import { jwtVerify } from "jose"; import { cookies } from "next/headers"; export async function funGetUserIdByToken() { - const c = cookies().get(process.env.NEXT_PUBLIC_BASE_SESSION_KEY!); + const c = cookies().get(process.env.NEXT_PUBLIC_BASE_SESSION_KEY!); + + const cekUser = await decrypt({ + token: c?.value as string, + encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, + }); + // const token = JSON.parse( // await unsealData(c?.value as string, { @@ -15,13 +22,32 @@ export async function funGetUserIdByToken() { // ); // return token.id; - const token = c?.value - const cekToken = await prisma.userSession.findFirst({ - where: { - token: token, - }, - }); + // const token = c?.value; + // const cekToken = await prisma.userSession.findFirst({ + // where: { + // token: token, + // }, + // }); // if (cekToken === null) return null - return cekToken?.userId + return cekUser?.id; +} + +async function decrypt({ + token, + encodedKey, +}: { + token: string; + encodedKey: string; +}): Promise | null> { + try { + const enc = new TextEncoder().encode(encodedKey); + const { payload } = await jwtVerify(token, enc, { + algorithms: ["HS256"], + }); + return (payload.user as Record) || null; + } catch (error) { + console.error("Gagal verifikasi session", error); + return null; + } } From 270065f46b72fc08b8a59632297c10e4f3bdd5db Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 14:26:55 +0800 Subject: [PATCH 010/595] chore(release): 1.2.16 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2be83116..63858849 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.16](https://github.com/bipproduction/hipmi/compare/v1.2.15...v1.2.16) (2024-12-03) + ## [1.2.15](https://github.com/bipproduction/hipmi/compare/v1.2.14...v1.2.15) (2024-12-03) ## [1.2.14](https://github.com/bipproduction/hipmi/compare/v1.2.13...v1.2.14) (2024-12-03) diff --git a/package.json b/package.json index d3b98567..3a2f0c4d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.15", + "version": "1.2.16", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From b638865c3345ad6d0f0615dc3fd322c5de88194f Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 3 Dec 2024 14:27:05 +0800 Subject: [PATCH 011/595] Fix version 1.2.16 --- src/app/dev/(user)/layout.tsx | 20 ------------- src/app/dev/katalog/[id]/layout.tsx | 2 ++ .../_component/comp_button_user_on_navbar.tsx | 17 +++++++++-- .../katalog/component/button_header_right.tsx | 3 ++ .../katalog/component/drawer_katalog.tsx | 28 ++++++++++++++++++- src/app_modules/katalog/main/layout.tsx | 3 ++ .../_component/comp_load_background.tsx | 4 ++- src/app_modules/katalog/ui/ui_profile.tsx | 1 + 8 files changed, 53 insertions(+), 25 deletions(-) delete mode 100644 src/app/dev/(user)/layout.tsx diff --git a/src/app/dev/(user)/layout.tsx b/src/app/dev/(user)/layout.tsx deleted file mode 100644 index 0017c962..00000000 --- a/src/app/dev/(user)/layout.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { RouterAdminDashboard } from "@/app/lib/router_hipmi/router_admin"; -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import { funGlobal_getUserById } from "@/app_modules/_global/fun/get/fun_get_user_by_id"; -import { redirect } from "next/navigation"; - -export default async function Layout({ - children, -}: { - children: React.ReactNode; -}) { - const userLoginId = await funGetUserIdByToken(); - const dataUser = await funGlobal_getUserById({ - userId: userLoginId as string, - }); - - if (dataUser?.masterUserRoleId != "1") - return redirect(RouterAdminDashboard.splash_admin); - - return <>{children}; -} diff --git a/src/app/dev/katalog/[id]/layout.tsx b/src/app/dev/katalog/[id]/layout.tsx index fd0d7fd4..bd874c57 100644 --- a/src/app/dev/katalog/[id]/layout.tsx +++ b/src/app/dev/katalog/[id]/layout.tsx @@ -14,6 +14,7 @@ export default async function Layout({ const authorId = dataProfile?.userId; const userLoginId = await funGetUserIdByToken(); + const userRoleId = dataProfile?.User?.masterUserRoleId; return ( <> @@ -21,6 +22,7 @@ export default async function Layout({ profileId={profileId} userLoginId={userLoginId as string} authorId={authorId as any} + userRoleId={userRoleId as string} > {children} diff --git a/src/app_modules/admin/_admin_global/_component/comp_button_user_on_navbar.tsx b/src/app_modules/admin/_admin_global/_component/comp_button_user_on_navbar.tsx index 787b63ce..3e840a5e 100644 --- a/src/app_modules/admin/_admin_global/_component/comp_button_user_on_navbar.tsx +++ b/src/app_modules/admin/_admin_global/_component/comp_button_user_on_navbar.tsx @@ -14,6 +14,7 @@ import { Menu, Modal, Popover, + SimpleGrid, Stack, Text, Title, @@ -82,11 +83,21 @@ export function Admin_ComponentButtonUserCircle({ -
- + -
+ diff --git a/src/app_modules/katalog/component/button_header_right.tsx b/src/app_modules/katalog/component/button_header_right.tsx index 02e90181..9c53d7b3 100644 --- a/src/app_modules/katalog/component/button_header_right.tsx +++ b/src/app_modules/katalog/component/button_header_right.tsx @@ -11,10 +11,12 @@ export function ComponentKatalog_ButtonHeaderRight({ profileId, userLoginId, authorId, + userRoleId, }: { profileId: string; userLoginId: string; authorId: string; + userRoleId: string }) { const [opened, { open, close }] = useDisclosure(); @@ -33,6 +35,7 @@ export function ComponentKatalog_ButtonHeaderRight({ close={() => close()} profileId={profileId} userId={userLoginId} + userRoleId={userRoleId} /> ); diff --git a/src/app_modules/katalog/component/drawer_katalog.tsx b/src/app_modules/katalog/component/drawer_katalog.tsx index 38be491d..b237ffa3 100644 --- a/src/app_modules/katalog/component/drawer_katalog.tsx +++ b/src/app_modules/katalog/component/drawer_katalog.tsx @@ -1,3 +1,4 @@ +import { RouterAdminDashboard } from "@/app/lib/router_hipmi/router_admin"; import { RouterPortofolio, RouterProfile, @@ -14,6 +15,7 @@ import { Text, } from "@mantine/core"; import { + IconDashboard, IconEdit, IconPencilPlus, IconPhotoEdit, @@ -27,16 +29,19 @@ export function ComponentKatalog_DrawerKatalog({ opened, close, profileId, - userId + userId, + userRoleId }: { opened: boolean; close: () => void; profileId: string; userId: string + userRoleId: string }) { const router = useRouter(); const [pageId, setPageId] = useState(""); const [isLoading, setIsLoading] = useState(false); + const [isLoadingAdmin, setIsLoadingAdmin] = useState(false); const listPage = [ { @@ -125,6 +130,27 @@ export function ComponentKatalog_DrawerKatalog({ ))} + {userRoleId != "1" && ( + + { + router.push(RouterAdminDashboard.main_admin, { scroll: false }); + setIsLoadingAdmin(true); + }} + > + {isLoadingAdmin ? ( + + ) : ( + + )} + + + Dashboard Admin + + + )} diff --git a/src/app_modules/katalog/main/layout.tsx b/src/app_modules/katalog/main/layout.tsx index 5ef20553..e84d845c 100644 --- a/src/app_modules/katalog/main/layout.tsx +++ b/src/app_modules/katalog/main/layout.tsx @@ -9,11 +9,13 @@ export default function KatalogLayout({ profileId, userLoginId, authorId, + userRoleId, }: { children: any; profileId: any; userLoginId: string; authorId: string; + userRoleId: string }) { return ( <> @@ -26,6 +28,7 @@ export default function KatalogLayout({ profileId={profileId} userLoginId={userLoginId} authorId={authorId as any} + userRoleId={userRoleId} /> } /> diff --git a/src/app_modules/katalog/profile/_component/comp_load_background.tsx b/src/app_modules/katalog/profile/_component/comp_load_background.tsx index bf09c029..ecace66c 100644 --- a/src/app_modules/katalog/profile/_component/comp_load_background.tsx +++ b/src/app_modules/katalog/profile/_component/comp_load_background.tsx @@ -10,13 +10,15 @@ import { useState } from "react"; export function Profile_ComponentLoadBackgroundImage({ fileId, + size }: { fileId: string; + size?: number }) { const router = useRouter(); const [isImage, setIsImage] = useState(null); const [isLoading, setLoading] = useState(false); - const url = APIs.GET({ fileId: fileId }); + const url = APIs.GET({ fileId: fileId,size: size ? size.toString() : "200" }); useShallowEffect(() => { onLoadImage(); diff --git a/src/app_modules/katalog/ui/ui_profile.tsx b/src/app_modules/katalog/ui/ui_profile.tsx index bc0be610..d844d433 100644 --- a/src/app_modules/katalog/ui/ui_profile.tsx +++ b/src/app_modules/katalog/ui/ui_profile.tsx @@ -68,6 +68,7 @@ export function Profile_UiView({ Date: Wed, 4 Dec 2024 11:12:52 +0800 Subject: [PATCH 012/595] Fix layout realtime --- package.json | 4 +- src/app/api/user/route.ts | 41 +++ src/app/dev/layout.tsx | 4 +- src/app/lib/realtime_provider.tsx | 283 ++++++++++-------- .../fun/get/fun_get_user_id_by_token.ts | 5 +- src/app_modules/event/_ui/konfirmasi.tsx | 3 - src/middleware.ts | 2 + 7 files changed, 204 insertions(+), 138 deletions(-) create mode 100644 src/app/api/user/route.ts diff --git a/package.json b/package.json index 3a2f0c4d..b9ead47c 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ }, "scripts": { "dev": "bun --bun run next dev --experimental-https", - "build": "bun --bun run next build", + "build": "NODE_OPTIONS='--max-old-space-size=2048' bun --bun run next build", "start": "bun --bun run next start", "lint": "bun --bun run next lint", "ver": "bunx commit-and-tag-version -- --prerelease" @@ -94,4 +94,4 @@ "wibu-pkg": "^1.0.3", "yaml": "^2.3.2" } -} +} \ No newline at end of file diff --git a/src/app/api/user/route.ts b/src/app/api/user/route.ts new file mode 100644 index 00000000..33b4d093 --- /dev/null +++ b/src/app/api/user/route.ts @@ -0,0 +1,41 @@ +import { jwtVerify } from "jose"; +import _ from "lodash"; +import { cookies } from "next/headers"; +import { NextResponse } from "next/server"; + +export async function GET() { + // const data = await req.text(); + // console.log(data); + const c = cookies().get(process.env.NEXT_PUBLIC_BASE_SESSION_KEY!); + + if (!c || !c?.value || _.isEmpty(c?.value) || _.isUndefined(c?.value)) { + return NextResponse.json({ status: 401, message: "Unauthorized" }); + } + + const token = c.value; + const dataUser = await decrypt({ + token: token, + encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, + }); + + return NextResponse.json({ status: 200, message: "OK", data: dataUser }); +} + +async function decrypt({ + token, + encodedKey, +}: { + token: string; + encodedKey: string; +}): Promise | null> { + try { + const enc = new TextEncoder().encode(encodedKey); + const { payload } = await jwtVerify(token, enc, { + algorithms: ["HS256"], + }); + return (payload.user as Record) || null; + } catch (error) { + console.error("Gagal verifikasi session", error); + return null; + } +} diff --git a/src/app/dev/layout.tsx b/src/app/dev/layout.tsx index 4c91bd10..aad4db0b 100644 --- a/src/app/dev/layout.tsx +++ b/src/app/dev/layout.tsx @@ -11,7 +11,7 @@ export default async function Layout({ }: { children: React.ReactNode; }) { - const userLoginId = await funGetUserIdByToken(); + // const userLoginId = await funGetUserIdByToken(); // const dataUser = await funGlobal_getUserById({ // userId: userLoginId as string, // }); @@ -27,7 +27,7 @@ export default async function Layout({ return ( <> { - // if (WIBU_REALTIME_TOKEN === undefined) return alert("gak dapet key"); - try { - WibuRealtime.init({ - project: "hipmi", - WIBU_REALTIME_TOKEN: WIBU_REALTIME_TOKEN, - onData(data: TypeNotification) { - if ( - data.type == "notification" && - data.pushNotificationTo == "ADMIN" - ) { - setNewAdminNtf((e) => e + 1); + onLoadUser({ + onSetUser(val: string) { + + if (val !== "" || val !== undefined) { + try { + WibuRealtime.init({ + project: "hipmi", + WIBU_REALTIME_TOKEN: WIBU_REALTIME_TOKEN, + onData(data: TypeNotification) { + if ( + data.type == "notification" && + data.pushNotificationTo == "ADMIN" + ) { + setNewAdminNtf((e) => e + 1); + } + + // Notifikasi ke semua user , yang datanya di acc admin + if ( + data.type == "notification" && + data.pushNotificationTo == "USER" && + data.dataMessage?.userId == userId + ) { + setNewUserNtf((e) => e + 1); + setDataRealtime(data.dataMessage as any); + } + + // ---------------------- JOB ------------------------- // + if ( + data.type == "trigger" && + data.pushNotificationTo == "ADMIN" && + data.dataMessage?.kategoriApp == "JOB" + ) { + setIsAdminJob_TriggerReview(true); + } + + if ( + data.type == "trigger" && + data.pushNotificationTo == "USER" && + data.dataMessage?.kategoriApp == "JOB" && + data.dataMessage.status == "Publish" + ) { + setIsTriggerJobBeranda(true); + } + // ---------------------- JOB ------------------------- // + + // ---------------------- EVENT ------------------------- // + if ( + data.type == "trigger" && + data.pushNotificationTo == "ADMIN" && + data.dataMessage?.kategoriApp == "EVENT" + ) { + setIsAdminEvent_TriggerReview(true); + } + + if ( + data.type == "trigger" && + data.pushNotificationTo == "USER" && + data.dataMessage?.kategoriApp == "EVENT" && + data.dataMessage.status == "Publish" + ) { + setIsTriggerEventBeranda(true); + } + + if ( + data.type == "notification" && + data.pushNotificationTo == "USER" && + data.dataMessage?.status == "Peserta Event" && + userId !== data.dataMessage?.userId + ) { + setNewUserNtf((e) => e + 1); + } + // ---------------------- EVENT ------------------------- // + + // ---------------------- VOTING ------------------------- // + if ( + data.type == "trigger" && + data.pushNotificationTo == "ADMIN" && + data.dataMessage?.kategoriApp == "VOTING" + ) { + setIsAdminVoting_TriggerReview(true); + } + + if ( + data.type == "trigger" && + data.pushNotificationTo == "USER" && + data.dataMessage?.kategoriApp == "VOTING" && + data.dataMessage.status == "Publish" + ) { + setIsTriggerVotingBeranda(true); + } + + if ( + data.type == "notification" && + data.pushNotificationTo == "USER" && + data.dataMessage?.status == "Voting Masuk" && + userId !== data.dataMessage?.userId + ) { + setNewUserNtf((e) => e + 1); + } + // ---------------------- VOTING ------------------------- // + + // ---------------------- DONASI ------------------------- // + if ( + data.type == "trigger" && + data.pushNotificationTo == "ADMIN" && + data.dataMessage?.kategoriApp == "DONASI" + ) { + setIsAdminDonasi_TriggerReview(true); + } + + if ( + data.type == "trigger" && + data.pushNotificationTo == "USER" && + data.dataMessage?.kategoriApp == "DONASI" && + data.dataMessage.status == "Publish" + ) { + setIsTriggerDonasiBeranda(true); + } + + // if ( + // data.type == "notification" && + // data.pushNotificationTo == "ADMIN" && + // data.dataMessage?.status == "Menunggu" && + // userLoginId !== data.dataMessage?.userId + // ) { + // console.log("yes"); + // } + + // ---------------------- DONASI ------------------------- // + }, + }); + } catch (error) { + console.log(error); } + } else { + return undefined; + } + }, + }); + }, [setUserId]); - // Notifikasi ke semua user , yang datanya di acc admin - if ( - data.type == "notification" && - data.pushNotificationTo == "USER" && - data.dataMessage?.userId == userLoginId - ) { - setNewUserNtf((e) => e + 1); - setDataRealtime(data.dataMessage as any); - } - - // ---------------------- JOB ------------------------- // - if ( - data.type == "trigger" && - data.pushNotificationTo == "ADMIN" && - data.dataMessage?.kategoriApp == "JOB" - ) { - setIsAdminJob_TriggerReview(true); - } - - if ( - data.type == "trigger" && - data.pushNotificationTo == "USER" && - data.dataMessage?.kategoriApp == "JOB" && - data.dataMessage.status == "Publish" - ) { - setIsTriggerJobBeranda(true); - } - // ---------------------- JOB ------------------------- // - - // ---------------------- EVENT ------------------------- // - if ( - data.type == "trigger" && - data.pushNotificationTo == "ADMIN" && - data.dataMessage?.kategoriApp == "EVENT" - ) { - setIsAdminEvent_TriggerReview(true); - } - - if ( - data.type == "trigger" && - data.pushNotificationTo == "USER" && - data.dataMessage?.kategoriApp == "EVENT" && - data.dataMessage.status == "Publish" - ) { - setIsTriggerEventBeranda(true); - } - - if ( - data.type == "notification" && - data.pushNotificationTo == "USER" && - data.dataMessage?.status == "Peserta Event" && - userLoginId !== data.dataMessage?.userId - ) { - setNewUserNtf((e) => e + 1); - } - // ---------------------- EVENT ------------------------- // - - // ---------------------- VOTING ------------------------- // - if ( - data.type == "trigger" && - data.pushNotificationTo == "ADMIN" && - data.dataMessage?.kategoriApp == "VOTING" - ) { - setIsAdminVoting_TriggerReview(true); - } - - if ( - data.type == "trigger" && - data.pushNotificationTo == "USER" && - data.dataMessage?.kategoriApp == "VOTING" && - data.dataMessage.status == "Publish" - ) { - setIsTriggerVotingBeranda(true); - } - - if ( - data.type == "notification" && - data.pushNotificationTo == "USER" && - data.dataMessage?.status == "Voting Masuk" && - userLoginId !== data.dataMessage?.userId - ) { - setNewUserNtf((e) => e + 1); - } - // ---------------------- VOTING ------------------------- // - - // ---------------------- DONASI ------------------------- // - if ( - data.type == "trigger" && - data.pushNotificationTo == "ADMIN" && - data.dataMessage?.kategoriApp == "DONASI" - ) { - setIsAdminDonasi_TriggerReview(true); - } - - if ( - data.type == "trigger" && - data.pushNotificationTo == "USER" && - data.dataMessage?.kategoriApp == "DONASI" && - data.dataMessage.status == "Publish" - ) { - setIsTriggerDonasiBeranda(true); - } - - // if ( - // data.type == "notification" && - // data.pushNotificationTo == "ADMIN" && - // data.dataMessage?.status == "Menunggu" && - // userLoginId !== data.dataMessage?.userId - // ) { - // console.log("yes"); - // } - - // ---------------------- DONASI ------------------------- // - }, - }); - } catch (error) { - console.log(error); - } - }, []); + async function onLoadUser({ + onSetUser, + }: { + onSetUser: (val: string) => void; + }) { + const res = await fetch("/api/user", { + method: "GET", + }); + const result = await res.json(); + onSetUser(result.data.id); + } return null; } diff --git a/src/app_modules/_global/fun/get/fun_get_user_id_by_token.ts b/src/app_modules/_global/fun/get/fun_get_user_id_by_token.ts index d08bc9cb..ed2d0ee0 100644 --- a/src/app_modules/_global/fun/get/fun_get_user_id_by_token.ts +++ b/src/app_modules/_global/fun/get/fun_get_user_id_by_token.ts @@ -7,13 +7,16 @@ import { jwtVerify } from "jose"; import { cookies } from "next/headers"; export async function funGetUserIdByToken() { - const c = cookies().get(process.env.NEXT_PUBLIC_BASE_SESSION_KEY!); + const SESSION_KEY = process.env.NEXT_PUBLIC_BASE_SESSION_KEY!; + // console.log("SESSION_KEY", SESSION_KEY); + const c = cookies().get("hipmi-key"); const cekUser = await decrypt({ token: c?.value as string, encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, }); + // console.log("userid" , cekUser?.id) // const token = JSON.parse( // await unsealData(c?.value as string, { diff --git a/src/app_modules/event/_ui/konfirmasi.tsx b/src/app_modules/event/_ui/konfirmasi.tsx index 468e8c14..3e1f3a43 100644 --- a/src/app_modules/event/_ui/konfirmasi.tsx +++ b/src/app_modules/event/_ui/konfirmasi.tsx @@ -74,9 +74,6 @@ export default function Ui_Konfirmasi({ } } - if (isPresent === null) { - return <>; - } return ( <> diff --git a/src/middleware.ts b/src/middleware.ts index 30cee1e5..11acde45 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -19,8 +19,10 @@ const middlewareConfig: MiddlewareConfig = { userPath: "/dev/home", publicRoutes: [ "/", + "/api/validation", "/api/auth/*", "/api/origin-url", + "/api/user", "/login", "/register", "/validasi", From 2e0a5c1c2ea09518af0ce0eb76e2949b7d0e514d Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Wed, 4 Dec 2024 11:13:51 +0800 Subject: [PATCH 013/595] chore(release): 1.2.17 --- CHANGELOG.md | 2 ++ package.json | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63858849..4d26ddfc 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.17](https://github.com/bipproduction/hipmi/compare/v1.2.16...v1.2.17) (2024-12-04) + ## [1.2.16](https://github.com/bipproduction/hipmi/compare/v1.2.15...v1.2.16) (2024-12-03) ## [1.2.15](https://github.com/bipproduction/hipmi/compare/v1.2.14...v1.2.15) (2024-12-03) diff --git a/package.json b/package.json index b9ead47c..663d9389 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.16", + "version": "1.2.17", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" @@ -94,4 +94,4 @@ "wibu-pkg": "^1.0.3", "yaml": "^2.3.2" } -} \ No newline at end of file +} From 9a9bbe3bd600c609a00a2b417797a438f8faa452 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Wed, 4 Dec 2024 15:45:33 +0800 Subject: [PATCH 014/595] chore(release): 1.2.18 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d26ddfc..108876d8 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.18](https://github.com/bipproduction/hipmi/compare/v1.2.17...v1.2.18) (2024-12-04) + ## [1.2.17](https://github.com/bipproduction/hipmi/compare/v1.2.16...v1.2.17) (2024-12-04) ## [1.2.16](https://github.com/bipproduction/hipmi/compare/v1.2.15...v1.2.16) (2024-12-03) diff --git a/package.json b/package.json index 663d9389..fb9c76ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.17", + "version": "1.2.18", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 5917a1b8d04294d833cc1898d637b922e325c9d0 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Wed, 4 Dec 2024 15:45:55 +0800 Subject: [PATCH 015/595] Fix : Event scanbarcode --- src/app/api/event/check-kehadiran/route.ts | 15 +++++ src/app/dev/event/konfirmasi/[id]/page.tsx | 6 +- .../event/table_status/table_publish.tsx | 56 ++++++++++++++----- src/app_modules/event/_ui/konfirmasi.tsx | 19 ++----- src/app_modules/event/main/beranda.tsx | 4 +- src/middleware.ts | 1 + 6 files changed, 71 insertions(+), 30 deletions(-) create mode 100644 src/app/api/event/check-kehadiran/route.ts diff --git a/src/app/api/event/check-kehadiran/route.ts b/src/app/api/event/check-kehadiran/route.ts new file mode 100644 index 00000000..3b2ef875 --- /dev/null +++ b/src/app/api/event/check-kehadiran/route.ts @@ -0,0 +1,15 @@ +import { event_funCheckKehadiran } from "@/app_modules/event/fun"; +import { NextResponse } from "next/server"; + +export async function GET(req: Request) { + const { searchParams } = new URL(req.url); + const userId = searchParams.get("userId"); + const eventId = searchParams.get("eventId"); + + const res = await event_funCheckKehadiran({ + eventId: eventId as string, + userId: userId as string, + }); + + return NextResponse.json({ res }); +} diff --git a/src/app/dev/event/konfirmasi/[id]/page.tsx b/src/app/dev/event/konfirmasi/[id]/page.tsx index a7de2574..eb04ea33 100644 --- a/src/app/dev/event/konfirmasi/[id]/page.tsx +++ b/src/app/dev/event/konfirmasi/[id]/page.tsx @@ -5,6 +5,7 @@ import { event_funCheckPesertaByUserId, } from "@/app_modules/event/fun"; import { event_getOneById } from "@/app_modules/event/fun/get/get_one_by_id"; +import moment from "moment"; import { redirect } from "next/navigation"; export default async function Page({ params }: { params: { id: string } }) { @@ -17,7 +18,10 @@ export default async function Page({ params }: { params: { id: string } }) { userId: userLoginId as string, }); - + if (dataEvent == null) return redirect("/dev/event/main/beranda"); + + if (moment(dataEvent?.tanggal).diff(moment(), "minutes") > 0) + return redirect("/dev/event/main/beranda"); if (dataEvent?.isArsip) return redirect(`/dev/event/detail/riwayat/${dataEvent.id}`); diff --git a/src/app_modules/admin/event/table_status/table_publish.tsx b/src/app_modules/admin/event/table_status/table_publish.tsx index 01316545..7bcd290b 100644 --- a/src/app_modules/admin/event/table_status/table_publish.tsx +++ b/src/app_modules/admin/event/table_status/table_publish.tsx @@ -50,20 +50,20 @@ function TableStatus({ listPublish }: { listPublish: any }) { const [eventId, setEventId] = useState(""); const [loading, setLoading] = useState(false); - const [origin, setOrigin] = useState(""); + const [origin, setOrigin] = useState(""); - useShallowEffect(() => { - onLoadOrigin(setOrigin); - // if (typeof window !== "undefined") { - // setOrigin(window.location.origin); - // } - }, [setOrigin]); + useShallowEffect(() => { + if (typeof window !== "undefined") { + console.log(window.location.origin); + setOrigin(window.location.origin); + } + }, [setOrigin]); - async function onLoadOrigin(setOrigin: any) { - const res = await fetch("/api/origin-url"); - const result = await res.json(); - setOrigin(result.origin); - } + // async function onLoadOrigin(setOrigin: any) { + // const res = await fetch("/api/origin-url"); + // const result = await res.json(); + // setOrigin(result.origin); + // } async function onSearch(s: string) { setSearch(s); @@ -97,11 +97,38 @@ function TableStatus({ listPublish }: { listPublish: any }) {
- + + +
+ { + const svg: any = document.getElementById(e.id); + const svgData = new XMLSerializer().serializeToString(svg); + const canvas = document.createElement("canvas"); + const ctx: any = canvas.getContext("2d"); + const img = new Image(); + img.onload = () => { + canvas.width = img.width; + canvas.height = img.height; + ctx.drawImage(img, 0, 0); + const pngFile = canvas.toDataURL("image/png"); + const downloadLink = document.createElement("a"); + downloadLink.download = `QRCode ${e.title}`; + downloadLink.href = `${pngFile}`; + downloadLink.click(); + }; + img.src = `data:image/svg+xml;base64,${btoa(svgData)}`; + }} + /> +
+
{e?.Author?.username} @@ -225,6 +252,9 @@ function TableStatus({ listPublish }: { listPublish: any }) {
QR Code
+ +
Download QR
+
Username
diff --git a/src/app_modules/event/_ui/konfirmasi.tsx b/src/app_modules/event/_ui/konfirmasi.tsx index 3e1f3a43..ec95fc00 100644 --- a/src/app_modules/event/_ui/konfirmasi.tsx +++ b/src/app_modules/event/_ui/konfirmasi.tsx @@ -30,14 +30,6 @@ export default function Ui_Konfirmasi({ const [isLoading, setLoading] = useState(false); const [isPresent, setIsPresent] = useState(null); - // useShallowEffect(() => { - // onLoadData({ - // onPublish(val) { - // setData(val); - // }, - // }); - // }, [setData]); - useShallowEffect(() => { onLoadKehadiran({ onChange(val) { @@ -51,12 +43,12 @@ export default function Ui_Konfirmasi({ }: { onChange: (val: boolean) => void; }) { - const checkKehadiran = await event_funCheckKehadiran({ - eventId: dataEvent.id, - userId: userLoginId as string, - }); + const res = await fetch( + `/api/event/check-kehadiran?userId=${userLoginId}&eventId=${dataEvent.id}` + ); + const checkKehadiran = await res.json(); - onChange(checkKehadiran); + onChange(checkKehadiran.res); } async function onUpdateKonfirmasi() { setLoading(true); @@ -74,6 +66,7 @@ export default function Ui_Konfirmasi({ } } + console.log(isPresent, "isPresent"); return ( <> diff --git a/src/app_modules/event/main/beranda.tsx b/src/app_modules/event/main/beranda.tsx index 5714cc6a..9a814ed8 100644 --- a/src/app_modules/event/main/beranda.tsx +++ b/src/app_modules/event/main/beranda.tsx @@ -20,7 +20,7 @@ export default function Event_Beranda({ }: { dataEvent: MODEL_EVENT[]; }) { - const [data, setData] = useState(dataEvent); + const [data, setData] = useState([]); const [activePage, setActivePage] = useState(1); const [isLoading, setIsLoading] = useState(false); @@ -76,8 +76,6 @@ export default function Event_Beranda({ setData(val); }, }); - - }} > Update beranda diff --git a/src/middleware.ts b/src/middleware.ts index 11acde45..d08b310f 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -23,6 +23,7 @@ const middlewareConfig: MiddlewareConfig = { "/api/auth/*", "/api/origin-url", "/api/user", + "/api/event/*", "/login", "/register", "/validasi", From 6fd2a81a593723013012463d161339f0d785380b Mon Sep 17 00:00:00 2001 From: amel Date: Wed, 4 Dec 2024 16:22:43 +0800 Subject: [PATCH 016/595] upd: home Deskripsi: - kompres gambar di home - clean code yg ga dipake No Issues --- public/aset/home/home-hipmi-new.png | Bin 0 -> 130912 bytes src/app/dev/(user)/home/page.tsx | 9 -------- src/app_modules/home/component/ui_home.tsx | 2 +- src/app_modules/home/view_home.tsx | 23 ++++++--------------- 4 files changed, 7 insertions(+), 27 deletions(-) create mode 100644 public/aset/home/home-hipmi-new.png diff --git a/public/aset/home/home-hipmi-new.png b/public/aset/home/home-hipmi-new.png new file mode 100644 index 0000000000000000000000000000000000000000..968807de426998b2db429e70e1973c3c17d971a0 GIT binary patch literal 130912 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

KQC8i<*W*|K2FHxQbEj<6DSl4=cCS>GrU6P!w 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 literal 0 HcmV?d00001 diff --git a/src/app/dev/(user)/home/page.tsx b/src/app/dev/(user)/home/page.tsx index 86f17573..dcfc7882 100644 --- a/src/app/dev/(user)/home/page.tsx +++ b/src/app/dev/(user)/home/page.tsx @@ -10,15 +10,6 @@ export default async function PageHome() { const dataJob = await job_getTwoForHomeView(); const countNotifikasi = await notifikasi_countUserNotifikasi(); - // console.log(userLoginId, "ini di home"); - // console.log(dataUser, "ini di home"); - - // if (dataUser?.active === false) { - // return redirect(RouterHome.home_user_non_active); - // } - - // if (dataUser?.masterUserRoleId === "2" || dataUser?.masterUserRoleId === "3") - // return redirect(RouterAdminDashboard.main_admin); return ( <> diff --git a/src/app_modules/home/component/ui_home.tsx b/src/app_modules/home/component/ui_home.tsx index 9ce1203d..a87ce053 100644 --- a/src/app_modules/home/component/ui_home.tsx +++ b/src/app_modules/home/component/ui_home.tsx @@ -104,7 +104,7 @@ export function Home_UiView({ border: `2px solid ${AccentColor.blue}`, }} > - logo + logo diff --git a/src/app_modules/home/view_home.tsx b/src/app_modules/home/view_home.tsx index 88e38bd1..4d6a5cc4 100644 --- a/src/app_modules/home/view_home.tsx +++ b/src/app_modules/home/view_home.tsx @@ -1,21 +1,15 @@ "use client"; - -import { useEffect, useState } from "react"; +import { gs_count_ntf, gs_user_ntf } from "@/app/lib/global_state"; +import { useShallowEffect } from "@mantine/hooks"; +import { useAtom } from "jotai"; +import { useState } from "react"; import UIGlobal_LayoutHeaderTamplate from "../_global/ui/ui_header_tamplate"; import UIGlobal_LayoutTamplate from "../_global/ui/ui_layout_tamplate"; import { MODEL_JOB } from "../job/model/interface"; -import { - ComponentHome_ButtonHeaderLeft, - ComponentHome_ButtonHeaderRight, -} from "./component/button_header"; +import notifikasi_countUserNotifikasi from "../notifikasi/fun/count/fun_count_by_id"; +import { ComponentHome_ButtonHeaderLeft, ComponentHome_ButtonHeaderRight, } from "./component/button_header"; import { Home_UiFooter, Home_UiView } from "./component/ui_home"; import { MODEL_USER } from "./model/interface"; -import { useShallowEffect } from "@mantine/hooks"; -import { gs_count_ntf, gs_user_ntf } from "@/app/lib/global_state"; -import { useAtom } from "jotai"; -import notifikasi_countUserNotifikasi from "../notifikasi/fun/count/fun_count_by_id"; -import { Center, Text, Title } from "@mantine/core"; -import { useRouter } from "next/navigation"; export default function HomeView({ dataUser, @@ -26,7 +20,6 @@ export default function HomeView({ dataJob: MODEL_JOB[]; countNotifikasi: number; }) { - const router = useRouter(); const [countNtf, setCountNtf] = useState(countNotifikasi); const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); @@ -60,15 +53,11 @@ export default function HomeView({ onLoad(loadNotif); } - // console.log(dataUser, "dipage") return ( <> - // HIPMI - //

Date: Wed, 4 Dec 2024 17:48:58 +0800 Subject: [PATCH 017/595] Fix beranda event --- src/app/api/event/get-all-by-userId/route.ts | 9 ++++ src/app_modules/event/main/beranda.tsx | 53 +++++++++++++------- 2 files changed, 44 insertions(+), 18 deletions(-) create mode 100644 src/app/api/event/get-all-by-userId/route.ts diff --git a/src/app/api/event/get-all-by-userId/route.ts b/src/app/api/event/get-all-by-userId/route.ts new file mode 100644 index 00000000..2ae65ce8 --- /dev/null +++ b/src/app/api/event/get-all-by-userId/route.ts @@ -0,0 +1,9 @@ +import { NextResponse } from "next/server"; + +export async function GET(params: Request) { + const { searchParams } = new URL(params.url); + const userId = searchParams.get("userId"); + + + return NextResponse.json({ userId }); +} \ No newline at end of file diff --git a/src/app_modules/event/main/beranda.tsx b/src/app_modules/event/main/beranda.tsx index 9a814ed8..3387a29b 100644 --- a/src/app_modules/event/main/beranda.tsx +++ b/src/app_modules/event/main/beranda.tsx @@ -5,7 +5,15 @@ import { RouterEvent } from "@/app/lib/router_hipmi/router_event"; import { AccentColor } from "@/app_modules/_global/color"; import ComponentGlobal_CreateButton from "@/app_modules/_global/component/button_create"; import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; -import { Affix, Box, Button, Center, Loader, rem } from "@mantine/core"; +import { + Affix, + Box, + Button, + Center, + Loader, + rem, + Skeleton, +} from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { useAtom } from "jotai"; import _ from "lodash"; @@ -20,7 +28,7 @@ export default function Event_Beranda({ }: { dataEvent: MODEL_EVENT[]; }) { - const [data, setData] = useState([]); + const [data, setData] = useState(null); const [activePage, setActivePage] = useState(1); const [isLoading, setIsLoading] = useState(false); @@ -31,23 +39,24 @@ export default function Event_Beranda({ const [isShowUpdate, setIsShowUpdate] = useState(false); useShallowEffect(() => { - onLoadData({ - onPublish(val) { - setData(val); - }, - }); - }, [setData]); + loadData(); + }, []); useShallowEffect(() => { if (isTriggerEventBeranda) { setIsShowUpdate(true); } - }, [isTriggerEventBeranda, setIsShowUpdate]); + }, [isTriggerEventBeranda]); - async function onLoadData({ onPublish }: { onPublish: (val: any) => void }) { + async function loadData() { + const loadData = await event_getListAllPublish({ page: 1 }); + setData(loadData as any); + } + + async function onLoadNewData() { setIsLoading(true); const loadData = await event_getListAllPublish({ page: 1 }); - onPublish(loadData); + setData(loadData as any); setIsShowUpdate(false); setIsTriggerEventBeranca(false); @@ -71,11 +80,7 @@ export default function Event_Beranda({ radius={"xl"} opacity={0.8} onClick={() => { - onLoadData({ - onPublish(val) { - setData(val); - }, - }); + onLoadNewData(); }} > Update beranda @@ -85,7 +90,19 @@ export default function Event_Beranda({ )} - {_.isEmpty(data) ? ( + + {data == null ? ( + Array.from({ length: 10 }).map((_, index) => ( + + )) + ) : _.isEmpty(data) ? ( ) : ( @@ -97,7 +114,7 @@ export default function Event_Beranda({
)} data={data} - setData={setData} + setData={setData as any} moreData={async () => { const loadData = await event_getListAllPublish({ page: activePage + 1, From 1e971c15265e8c7add5ad4d6e42dd5b45dbc5a47 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 5 Dec 2024 12:11:40 +0800 Subject: [PATCH 018/595] Fix prisma Deskripsi: - Cek pemanggilan prisma di develop mode --- next.config.js | 2 +- package.json | 3 +- src/app/dev/layout.tsx | 24 +-- src/app/lib/new_fun_user_id.ts | 38 ++++ src/app/lib/prisma.ts | 33 ++-- src/app/lib/realtime_provider.tsx | 281 ++++++++++++++---------------- 6 files changed, 194 insertions(+), 187 deletions(-) create mode 100644 src/app/lib/new_fun_user_id.ts diff --git a/next.config.js b/next.config.js index 5c728631..b00f93a2 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: false, + // reactStrictMode: false, experimental: { serverActions: true }, diff --git a/package.json b/package.json index fb9c76ac..bb7c07b7 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "scripts": { "dev": "bun --bun run next dev --experimental-https", "build": "NODE_OPTIONS='--max-old-space-size=2048' bun --bun run next build", + "build:dev": "bun --bun run next build", "start": "bun --bun run next start", "lint": "bun --bun run next lint", "ver": "bunx commit-and-tag-version -- --prerelease" @@ -94,4 +95,4 @@ "wibu-pkg": "^1.0.3", "yaml": "^2.3.2" } -} +} \ No newline at end of file diff --git a/src/app/dev/layout.tsx b/src/app/dev/layout.tsx index aad4db0b..1ac8606a 100644 --- a/src/app/dev/layout.tsx +++ b/src/app/dev/layout.tsx @@ -1,42 +1,24 @@ -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import { funGlobal_getUserById } from "@/app_modules/_global/fun/get/fun_get_user_by_id"; -import { redirect } from "next/navigation"; import { RealtimeProvider } from "../lib"; +import { newFunGetUserId } from "../lib/new_fun_user_id"; import { ServerEnv } from "../lib/server_env"; -import { RouterAdminDashboard } from "../lib/router_hipmi/router_admin"; -import { funGlobal_checkActivationUseById } from "@/app_modules/_global/fun/get/fun_check_activation_use_by_id"; export default async function Layout({ children, }: { children: React.ReactNode; }) { - // const userLoginId = await funGetUserIdByToken(); - // const dataUser = await funGlobal_getUserById({ - // userId: userLoginId as string, - // }); - - // if (dataUser?.masterUserRoleId != "1") return redirect("/dev/home"); - - // const activationUser = await funGlobal_checkActivationUseById({ - // userId: userLoginId as string, - // }); - - // if (activationUser == false) return redirect("/waiting-room"); + const userLoginId = await newFunGetUserId(); return ( <> {children} - {/* - {children} - */} ); } diff --git a/src/app/lib/new_fun_user_id.ts b/src/app/lib/new_fun_user_id.ts new file mode 100644 index 00000000..188afff5 --- /dev/null +++ b/src/app/lib/new_fun_user_id.ts @@ -0,0 +1,38 @@ +import { jwtVerify } from "jose"; +import _ from "lodash"; +import { cookies } from "next/headers"; + +export async function newFunGetUserId() { + const c = cookies().get(process.env.NEXT_PUBLIC_BASE_SESSION_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; +} + +async function decrypt({ + token, + encodedKey, +}: { + token: string; + encodedKey: string; +}): Promise | null> { + try { + const enc = new TextEncoder().encode(encodedKey); + const { payload } = await jwtVerify(token, enc, { + algorithms: ["HS256"], + }); + return (payload.user as Record) || null; + } catch (error) { + console.error("Gagal verifikasi session", error); + return null; + } +} diff --git a/src/app/lib/prisma.ts b/src/app/lib/prisma.ts index 553b19ad..a0d1a749 100644 --- a/src/app/lib/prisma.ts +++ b/src/app/lib/prisma.ts @@ -1,17 +1,26 @@ -import { PrismaClient } from '@prisma/client' - -const prismaClientSingleton = () => { - return new PrismaClient() -} - -type PrismaClientSingleton = ReturnType +import { PrismaClient } from '@prisma/client'; +// Singleton PrismaClient untuk pengembangan const globalForPrisma = globalThis as unknown as { - prisma: PrismaClientSingleton | undefined + __prisma__: PrismaClient | undefined; +}; + +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 (!globalForPrisma.__prisma__) { + console.log('PrismaClient initialized in development mode'); + } + globalForPrisma.__prisma__ = prisma; } -const prisma = globalForPrisma.prisma ?? prismaClientSingleton() +process.on('SIGINT', async () => { + console.log('Disconnecting PrismaClient...'); + await prisma.$disconnect(); + process.exit(0); +}); -export default prisma - -if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma \ No newline at end of file +export default prisma; diff --git a/src/app/lib/realtime_provider.tsx b/src/app/lib/realtime_provider.tsx index ba302d45..42b32e7a 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 { useState } from "react"; -import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; // const WIBU_REALTIME_TOKEN: string | undefined = // process.env.NEXT_PUBLIC_WIBU_REALTIME_TOKEN; @@ -38,7 +36,6 @@ export default function RealtimeProvider({ userLoginId: string; WIBU_REALTIME_TOKEN: string; }) { - const [userId, setUserId] = useState(""); const [dataRealtime, setDataRealtime] = useAtom(gs_realtimeData); const [newAdminNtf, setNewAdminNtf] = useAtom(gs_admin_ntf); const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); @@ -75,158 +72,138 @@ export default function RealtimeProvider({ ); useShallowEffect(() => { - onLoadUser({ - onSetUser(val: string) { - if (val !== "" || val !== undefined) { - try { - WibuRealtime.init({ - project: "hipmi", - WIBU_REALTIME_TOKEN: WIBU_REALTIME_TOKEN, - onData(data: TypeNotification) { - if ( - data.type == "notification" && - data.pushNotificationTo == "ADMIN" - ) { - setNewAdminNtf((e) => e + 1); - } - - // Notifikasi ke semua user , yang datanya di acc admin - if ( - data.type == "notification" && - data.pushNotificationTo == "USER" && - data.dataMessage?.userId == userId - ) { - setNewUserNtf((e) => e + 1); - setDataRealtime(data.dataMessage as any); - } - - // ---------------------- JOB ------------------------- // - if ( - data.type == "trigger" && - data.pushNotificationTo == "ADMIN" && - data.dataMessage?.kategoriApp == "JOB" - ) { - setIsAdminJob_TriggerReview(true); - } - - if ( - data.type == "trigger" && - data.pushNotificationTo == "USER" && - data.dataMessage?.kategoriApp == "JOB" && - data.dataMessage.status == "Publish" - ) { - setIsTriggerJobBeranda(true); - } - // ---------------------- JOB ------------------------- // - - // ---------------------- EVENT ------------------------- // - if ( - data.type == "trigger" && - data.pushNotificationTo == "ADMIN" && - data.dataMessage?.kategoriApp == "EVENT" - ) { - setIsAdminEvent_TriggerReview(true); - } - - if ( - data.type == "trigger" && - data.pushNotificationTo == "USER" && - data.dataMessage?.kategoriApp == "EVENT" && - data.dataMessage.status == "Publish" - ) { - setIsTriggerEventBeranda(true); - } - - if ( - data.type == "notification" && - data.pushNotificationTo == "USER" && - data.dataMessage?.status == "Peserta Event" && - userId !== data.dataMessage?.userId - ) { - setNewUserNtf((e) => e + 1); - } - // ---------------------- EVENT ------------------------- // - - // ---------------------- VOTING ------------------------- // - if ( - data.type == "trigger" && - data.pushNotificationTo == "ADMIN" && - data.dataMessage?.kategoriApp == "VOTING" - ) { - setIsAdminVoting_TriggerReview(true); - } - - if ( - data.type == "trigger" && - data.pushNotificationTo == "USER" && - data.dataMessage?.kategoriApp == "VOTING" && - data.dataMessage.status == "Publish" - ) { - setIsTriggerVotingBeranda(true); - } - - if ( - data.type == "notification" && - data.pushNotificationTo == "USER" && - data.dataMessage?.status == "Voting Masuk" && - userId !== data.dataMessage?.userId - ) { - setNewUserNtf((e) => e + 1); - } - // ---------------------- VOTING ------------------------- // - - // ---------------------- DONASI ------------------------- // - if ( - data.type == "trigger" && - data.pushNotificationTo == "ADMIN" && - data.dataMessage?.kategoriApp == "DONASI" - ) { - setIsAdminDonasi_TriggerReview(true); - } - - if ( - data.type == "trigger" && - data.pushNotificationTo == "USER" && - data.dataMessage?.kategoriApp == "DONASI" && - data.dataMessage.status == "Publish" - ) { - setIsTriggerDonasiBeranda(true); - } - - // if ( - // data.type == "notification" && - // data.pushNotificationTo == "ADMIN" && - // data.dataMessage?.status == "Menunggu" && - // userLoginId !== data.dataMessage?.userId - // ) { - // console.log("yes"); - // } - - // ---------------------- DONASI ------------------------- // - }, - }); - } catch (error) { - console.log(error); + try { + WibuRealtime.init({ + project: "hipmi", + WIBU_REALTIME_TOKEN: WIBU_REALTIME_TOKEN, + onData(data: TypeNotification) { + if ( + data.type == "notification" && + data.pushNotificationTo == "ADMIN" + ) { + setNewAdminNtf((e) => e + 1); } - } else { - return undefined; - } - }, - }); - }, [setUserId]); - async function onLoadUser({ - onSetUser, - }: { - onSetUser: (val: string) => void; - }) { - const res = await fetch("/api/user", { - method: "GET", - }); - const result = await res.json(); - onSetUser(result.data.id); - } + // Notifikasi ke semua user , yang datanya di acc admin + if ( + data.type == "notification" && + data.pushNotificationTo == "USER" && + data.dataMessage?.userId == userLoginId + ) { + setNewUserNtf((e) => e + 1); + setDataRealtime(data.dataMessage as any); + } + + // ---------------------- JOB ------------------------- // + if ( + data.type == "trigger" && + data.pushNotificationTo == "ADMIN" && + data.dataMessage?.kategoriApp == "JOB" + ) { + setIsAdminJob_TriggerReview(true); + } + + if ( + data.type == "trigger" && + data.pushNotificationTo == "USER" && + data.dataMessage?.kategoriApp == "JOB" && + data.dataMessage.status == "Publish" + ) { + setIsTriggerJobBeranda(true); + } + // ---------------------- JOB ------------------------- // + + // ---------------------- EVENT ------------------------- // + if ( + data.type == "trigger" && + data.pushNotificationTo == "ADMIN" && + data.dataMessage?.kategoriApp == "EVENT" + ) { + setIsAdminEvent_TriggerReview(true); + } + + if ( + data.type == "trigger" && + data.pushNotificationTo == "USER" && + data.dataMessage?.kategoriApp == "EVENT" && + data.dataMessage.status == "Publish" + ) { + setIsTriggerEventBeranda(true); + } + + if ( + data.type == "notification" && + data.pushNotificationTo == "USER" && + data.dataMessage?.status == "Peserta Event" && + userLoginId !== data.dataMessage?.userId + ) { + setNewUserNtf((e) => e + 1); + } + // ---------------------- EVENT ------------------------- // + + // ---------------------- VOTING ------------------------- // + if ( + data.type == "trigger" && + data.pushNotificationTo == "ADMIN" && + data.dataMessage?.kategoriApp == "VOTING" + ) { + setIsAdminVoting_TriggerReview(true); + } + + if ( + data.type == "trigger" && + data.pushNotificationTo == "USER" && + data.dataMessage?.kategoriApp == "VOTING" && + data.dataMessage.status == "Publish" + ) { + setIsTriggerVotingBeranda(true); + } + + if ( + data.type == "notification" && + data.pushNotificationTo == "USER" && + data.dataMessage?.status == "Voting Masuk" && + userLoginId !== data.dataMessage?.userId + ) { + setNewUserNtf((e) => e + 1); + } + // ---------------------- VOTING ------------------------- // + + // ---------------------- DONASI ------------------------- // + if ( + data.type == "trigger" && + data.pushNotificationTo == "ADMIN" && + data.dataMessage?.kategoriApp == "DONASI" + ) { + setIsAdminDonasi_TriggerReview(true); + } + + if ( + data.type == "trigger" && + data.pushNotificationTo == "USER" && + data.dataMessage?.kategoriApp == "DONASI" && + data.dataMessage.status == "Publish" + ) { + setIsTriggerDonasiBeranda(true); + } + + // if ( + // data.type == "notification" && + // data.pushNotificationTo == "ADMIN" && + // data.dataMessage?.status == "Menunggu" && + // userLoginId !== data.dataMessage?.userId + // ) { + // console.log("yes"); + // } + + // ---------------------- DONASI ------------------------- // + }, + }); + } catch (error) { + console.log("Error!:", error); + } + }, []); return null; } From 59584f246120432d68b5c204c6072804124b9576 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 5 Dec 2024 14:27:57 +0800 Subject: [PATCH 019/595] Fix prisma Deskripsi: - Fun user id di buat baru dan mengambil dari cookies --- next.config.js | 2 +- src/app/dev/layout.tsx | 3 --- src/app/lib/new_fun_user_id.ts | 25 ++++--------------------- src/app/lib/realtime_provider.tsx | 12 ++++++++++-- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/next.config.js b/next.config.js index b00f93a2..5c728631 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - // reactStrictMode: false, + reactStrictMode: false, experimental: { serverActions: true }, diff --git a/src/app/dev/layout.tsx b/src/app/dev/layout.tsx index 1ac8606a..49f4a4c7 100644 --- a/src/app/dev/layout.tsx +++ b/src/app/dev/layout.tsx @@ -7,12 +7,9 @@ export default async function Layout({ }: { children: React.ReactNode; }) { - const userLoginId = await newFunGetUserId(); - return ( <> | null> { - try { - const enc = new TextEncoder().encode(encodedKey); - const { payload } = await jwtVerify(token, enc, { - algorithms: ["HS256"], - }); - return (payload.user as Record) || null; - } catch (error) { - console.error("Gagal verifikasi session", error); - return null; - } -} +} \ No newline at end of file diff --git a/src/app/lib/realtime_provider.tsx b/src/app/lib/realtime_provider.tsx index 42b32e7a..07919a7f 100644 --- a/src/app/lib/realtime_provider.tsx +++ b/src/app/lib/realtime_provider.tsx @@ -17,6 +17,8 @@ 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; @@ -30,12 +32,11 @@ export type TypeNotification = { }; export default function RealtimeProvider({ - userLoginId, WIBU_REALTIME_TOKEN, }: { - userLoginId: 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); @@ -71,7 +72,14 @@ export default function RealtimeProvider({ gs_donasiTriggerBeranda ); + async function loadUserId() { + const userId = await newFunGetUserId(); + + setUserLoginId(userId as string); + } + useShallowEffect(() => { + loadUserId(); try { WibuRealtime.init({ From 2fa96d47baa91a5c90a2ac9a15c3f8beae69a15f Mon Sep 17 00:00:00 2001 From: amel Date: Thu, 5 Dec 2024 15:12:18 +0800 Subject: [PATCH 020/595] fix :home Deskripsi: - home pake api No Issues --- src/app/api/new/home/route.ts | 68 ++++++ src/app/api/new/user/route.ts | 61 ++++++ src/app/dev/(user)/home/page.tsx | 16 +- src/app_modules/home/component/body_home.tsx | 193 ++++++++++++++++++ .../home/component/footer_home.tsx | 113 ++++++++++ .../home/component/list_menu_home.tsx | 67 ++++++ src/app_modules/home/fun/get/api_home.ts | 4 + src/app_modules/home/index.ts | 4 +- src/app_modules/home/view_home_new.tsx | 127 ++++++++++++ src/app_modules/user/index.ts | 5 + src/app_modules/user/lib/api_user.ts | 4 + src/app_modules/user/lib/type_user.ts | 14 ++ src/middleware.ts | 1 + 13 files changed, 667 insertions(+), 10 deletions(-) create mode 100644 src/app/api/new/home/route.ts create mode 100644 src/app/api/new/user/route.ts create mode 100644 src/app_modules/home/component/body_home.tsx create mode 100644 src/app_modules/home/component/footer_home.tsx create mode 100644 src/app_modules/home/component/list_menu_home.tsx create mode 100644 src/app_modules/home/fun/get/api_home.ts create mode 100644 src/app_modules/home/view_home_new.tsx create mode 100644 src/app_modules/user/index.ts create mode 100644 src/app_modules/user/lib/api_user.ts create mode 100644 src/app_modules/user/lib/type_user.ts diff --git a/src/app/api/new/home/route.ts b/src/app/api/new/home/route.ts new file mode 100644 index 00000000..3e07f313 --- /dev/null +++ b/src/app/api/new/home/route.ts @@ -0,0 +1,68 @@ +import { prisma } from "@/app/lib"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import { NextResponse } from "next/server"; + + +// GET DATA HOME +export async function GET(request: Request) { + try { + let fixData + const { searchParams } = new URL(request.url) + 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 == "job") { + fixData = await prisma.job.findMany({ + take: 2, + orderBy: { + createdAt: "desc", + }, + where: { + isActive: true, + masterStatusId: "1" + }, + select: { + id: true, + Author: { + select: { + id: true, + username: true, + }, + }, + title: true, + deskripsi: true + }, + }); + } else if (kategori == "cek_profile") { + const data = await prisma.user.findUnique({ + where: { + id: userLoginId, + }, + select: { + Profile: { + select: { + id: true, + imageId: true, + } + } + } + }); + + fixData = { + profile: data?.Profile?.id, + imageId: data?.Profile?.imageId + } + + } + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: fixData }, { 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/user/route.ts b/src/app/api/new/user/route.ts new file mode 100644 index 00000000..8a343879 --- /dev/null +++ b/src/app/api/new/user/route.ts @@ -0,0 +1,61 @@ +import { prisma } from "@/app/lib"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import { NextResponse } from "next/server"; + + +// GET DATA USER LOGIN +export async function GET(request: Request) { + try { + 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.user.findFirst({ + where: { + id: userLoginId, + }, + select: { + id: true, + username: true, + nomor: true, + active: true, + masterUserRoleId: true, + Profile: { + select: { + id: true, + name: true, + email: true, + alamat: true, + jenisKelamin: true, + imageId: true, + imageBackgroundId: true + } + } + } + }); + + const dataFix = { + id: data?.id, + username: data?.username, + nomor: data?.nomor, + active: data?.active, + masterUserRoleId: data?.masterUserRoleId, + idProfile: data?.Profile?.id, + name: data?.Profile?.name, + email: data?.Profile?.email, + alamat: data?.Profile?.alamat, + jenisKelamin: data?.Profile?.jenisKelamin, + imageId: data?.Profile?.imageId, + imageBackgroundId: data?.Profile?.imageBackgroundId + + } + + 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/dev/(user)/home/page.tsx b/src/app/dev/(user)/home/page.tsx index dcfc7882..4bd78e32 100644 --- a/src/app/dev/(user)/home/page.tsx +++ b/src/app/dev/(user)/home/page.tsx @@ -1,23 +1,21 @@ -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import { HomeView } from "@/app_modules/home"; -import { user_getOneByUserId } from "@/app_modules/home/fun/get/get_one_user_by_id"; -import { job_getTwoForHomeView } from "@/app_modules/job/fun/get/get_two_for_home_view"; +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 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_modules/home/component/body_home.tsx b/src/app_modules/home/component/body_home.tsx new file mode 100644 index 00000000..d5984b8d --- /dev/null +++ b/src/app_modules/home/component/body_home.tsx @@ -0,0 +1,193 @@ +import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { ActionIcon, Box, Group, Image, Paper, SimpleGrid, Skeleton, Stack, Text } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconUserSearch } from "@tabler/icons-react"; +import _ from "lodash"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { apiGetDataHome } from "../fun/get/api_home"; +import { listMenuHomeBody, menuHomeJob } from "./list_menu_home"; + +export default function BodyHome() { + const router = useRouter() + const [dataUser, setDataUser] = useState({}) + const [dataJob, setDataJob] = useState([]) + const [loadingJob, setLoadingJob] = useState(true) + + useShallowEffect(() => { + cekUserLogin() + getHomeJob() + }, []); + + + async function cekUserLogin() { + try { + const response = await apiGetDataHome("?cat=cek_profile") + if (response.success) { + setDataUser(response.data); + } + } catch (error) { + console.error(error); + } + } + + async function getHomeJob() { + try { + setLoadingJob(true) + const response = await apiGetDataHome("?cat=job") + if (response.success) { + setDataJob(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoadingJob(false) + } + } + + return ( + + + logo + + + + + {listMenuHomeBody.map((e, i) => ( + { + if (dataUser.profile === undefined || dataUser?.profile === null) { + return ComponentGlobal_NotifikasiPeringatan( + "Lengkapi Profile" + ); + } else { + if (e.link === "") { + return ComponentGlobal_NotifikasiPeringatan( + "Cooming Soon !!" + ); + } else { + router.push(e.link, { scroll: false }); + } + } + }} + > + + + {e.icon} + + + {e.name} + + + + ))} + + + {/* Job View */} + + { + if (dataUser.profile === undefined || dataUser?.profile === null) { + return ComponentGlobal_NotifikasiPeringatan( + "Lengkapi Profile" + ); + } else { + if (menuHomeJob.link === "") { + return ComponentGlobal_NotifikasiPeringatan( + "Cooming Soon !!" + ); + } else { + return router.push(menuHomeJob.link, { scroll: false }); + } + } + }} + > + + + {menuHomeJob.icon} + + + {menuHomeJob.name} + + + { + loadingJob ? + Array(2) + .fill(null) + .map((_, i) => ( + + + + + + )) + : _.isEmpty(dataJob) ? + () + : ( + + {dataJob.map((e, i) => ( + + + + + + + + {e?.Author.username} + + + {e?.title} + + + + + ))} + + ) + } + + + + + ); +} \ No newline at end of file diff --git a/src/app_modules/home/component/footer_home.tsx b/src/app_modules/home/component/footer_home.tsx new file mode 100644 index 00000000..f116b19d --- /dev/null +++ b/src/app_modules/home/component/footer_home.tsx @@ -0,0 +1,113 @@ +import { APIs } from "@/app/lib"; +import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { ActionIcon, Box, Center, SimpleGrid, Stack, Text } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconUserCircle } from "@tabler/icons-react"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { apiGetDataHome } from "../fun/get/api_home"; +import { Home_ComponentAvatarProfile } from "./comp_avatar_profile"; +import { listMenuHomeFooter } from "./list_menu_home"; + +export default function FooterHome() { + const router = useRouter() + const [dataUser, setDataUser] = useState({}) + + useShallowEffect(() => { + cekUserLogin(); + }, []); + + + async function cekUserLogin() { + try { + const response = await apiGetDataHome("?cat=cek_profile") + if (response.success) { + setDataUser(response.data); + } + } catch (error) { + console.error(error); + } + } + + return ( + + + {listMenuHomeFooter.map((e) => ( +
+ { + if (dataUser.profile === undefined || dataUser?.profile === null) { + ComponentGlobal_NotifikasiPeringatan("Lengkapi Profile"); + } else { + if (e.link == "") { + ComponentGlobal_NotifikasiPeringatan("Cooming Soon") + } else { + router.push(e.link, { scroll: false }) + } + } + }} + > + + {e.icon} + + + {e.name} + + +
+ ))} + +
+ { + if (dataUser.profile === undefined || dataUser?.profile === null) { + router.push(RouterProfile.create, { scroll: false }); + } else { + router.push( + RouterProfile.katalogOLD + `${dataUser?.profile}`, + { scroll: false } + ); + } + }} + > + + {dataUser.profile === undefined || dataUser?.profile === null + ? + + : + + } + + + Profile + + +
+
+
+ ); +} \ No newline at end of file diff --git a/src/app_modules/home/component/list_menu_home.tsx b/src/app_modules/home/component/list_menu_home.tsx new file mode 100644 index 00000000..8b9de429 --- /dev/null +++ b/src/app_modules/home/component/list_menu_home.tsx @@ -0,0 +1,67 @@ +import { RouterColab } from '@/app/lib/router_hipmi/router_colab'; +import { RouterEvent } from '@/app/lib/router_hipmi/router_event'; +import { RouterForum } from '@/app/lib/router_hipmi/router_forum'; +import { RouterJob } from '@/app/lib/router_hipmi/router_job'; +import { RouterMap } from '@/app/lib/router_hipmi/router_map'; +import { RouterVote } from '@/app/lib/router_hipmi/router_vote'; +import { IconAffiliate, IconBriefcase, IconHeartHandshake, IconMap2, IconMessages, IconPackageImport, IconPresentation, IconShoppingBag } from '@tabler/icons-react'; + + +// yg ada di footer home +export const listMenuHomeFooter = [ + { + id: 1, + name: "Forums", + icon: , + link: RouterForum.splash, + }, + { + id: 2, + name: "MarketPlace", + icon: , + link: "", + }, + { + id: 3, + name: "Business Maps", + icon: , + link: RouterMap.splash, + }, +]; + + +// yg ada di kotak2 home (body) +export const listMenuHomeBody = [ + { + id: 1, + name: "Event", + icon: , + link: RouterEvent.splash, + }, + { + id: 2, + name: "Collaboration", + icon: , + link: RouterColab.splash, + }, + + { + id: 3, + name: "Voting", + icon: , + link: RouterVote.splash, + }, + + { + id: 4, + name: "Crowd Funding", + icon: , + link: `/dev/crowd/splash`, + }, +]; + +export const menuHomeJob = { + name: "Job Vacancy", + icon: , + link: RouterJob.spalsh, +}; \ No newline at end of file diff --git a/src/app_modules/home/fun/get/api_home.ts b/src/app_modules/home/fun/get/api_home.ts new file mode 100644 index 00000000..e55ec5eb --- /dev/null +++ b/src/app_modules/home/fun/get/api_home.ts @@ -0,0 +1,4 @@ +export const apiGetDataHome = async (path?: string) => { + const response = await fetch(`/api/new/home${(path) ? path : ''}`) + return await response.json().catch(() => null) +} \ No newline at end of file diff --git a/src/app_modules/home/index.ts b/src/app_modules/home/index.ts index 07785dd4..e0eda0b5 100644 --- a/src/app_modules/home/index.ts +++ b/src/app_modules/home/index.ts @@ -1,4 +1,6 @@ +import { listMenuHomeFooter } from './component/list_menu_home'; import HomeView from "./view_home"; +import HomeViewNew from "./view_home_new"; import Home_UserNotActive from "./user_non_active"; -export { HomeView, Home_UserNotActive as Home_UserNonActive }; +export { HomeView, HomeViewNew, listMenuHomeFooter, Home_UserNotActive as Home_UserNonActive }; diff --git a/src/app_modules/home/view_home_new.tsx b/src/app_modules/home/view_home_new.tsx new file mode 100644 index 00000000..8fbe5441 --- /dev/null +++ b/src/app_modules/home/view_home_new.tsx @@ -0,0 +1,127 @@ +"use client"; +import { gs_count_ntf, gs_user_ntf } from "@/app/lib/global_state"; +import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi"; +import { RouterUserSearch } from "@/app/lib/router_hipmi/router_user_search"; +import { ActionIcon, Indicator, Text } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconBell, IconUserSearch } from "@tabler/icons-react"; +import { useAtom } from "jotai"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { MainColor } from "../_global/color"; +import { ComponentGlobal_NotifikasiPeringatan } from "../_global/notif_global"; +import UIGlobal_LayoutHeaderTamplate from "../_global/ui/ui_header_tamplate"; +import UIGlobal_LayoutTamplate from "../_global/ui/ui_layout_tamplate"; +import notifikasi_countUserNotifikasi from "../notifikasi/fun/count/fun_count_by_id"; +import BodyHome from "./component/body_home"; +import FooterHome from "./component/footer_home"; +import { apiGetDataHome } from "./fun/get/api_home"; + +export default function HomeViewNew({ countNotifikasi }: { countNotifikasi: number; }) { + const [countNtf, setCountNtf] = useState(countNotifikasi); + const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); + const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); + const [dataUser, setDataUser] = useState({}) + const router = useRouter(); + + + useShallowEffect(() => { + onLoadNotifikasi({ + onLoad(val) { + setCountNtf(val); + }, + }); + + setCountNtf(countLoadNtf as any); + }, [countLoadNtf, setCountNtf]); + + useShallowEffect(() => { + setCountNtf(countNtf + newUserNtf); + setNewUserNtf(0); + }, [newUserNtf, setCountNtf]); + + async function onLoadNotifikasi({ onLoad }: { onLoad: (val: any) => void }) { + const loadNotif = await notifikasi_countUserNotifikasi(); + onLoad(loadNotif); + } + + useShallowEffect(() => { + cekUserLogin(); + }, []); + + + async function cekUserLogin() { + try { + const response = await apiGetDataHome("?cat=cek_profile") + if (response.success) { + setDataUser(response.data); + } + } catch (error) { + console.error(error); + } + } + + return ( + <> + { + if (dataUser.profile === undefined || dataUser?.profile === null) { + ComponentGlobal_NotifikasiPeringatan("Lengkapi Profile"); + } else { + router.push(RouterUserSearch.main, { scroll: false }); + } + }} + > + + + } + customButtonRight={ + { + if (dataUser.profile === undefined || dataUser?.profile === null) { + ComponentGlobal_NotifikasiPeringatan("Lengkapi Profile"); + } else { + router.push(RouterNotifikasi.categoryApp({ name: "semua" }), { + scroll: false, + }); + } + }} + > + { + countNotifikasi > 0 + ? + + {countNotifikasi > 99 ? "99+" : countNotifikasi} + + } + > + + + : + + } + + + } + /> + } + + footer={} + > + + + + ); +} diff --git a/src/app_modules/user/index.ts b/src/app_modules/user/index.ts new file mode 100644 index 00000000..343e274c --- /dev/null +++ b/src/app_modules/user/index.ts @@ -0,0 +1,5 @@ +import { IUserLogin } from './lib/type_user'; +import { apiGetUserLogin } from "./lib/api_user"; + +export { apiGetUserLogin }; +export type { IUserLogin }; diff --git a/src/app_modules/user/lib/api_user.ts b/src/app_modules/user/lib/api_user.ts new file mode 100644 index 00000000..8db7415b --- /dev/null +++ b/src/app_modules/user/lib/api_user.ts @@ -0,0 +1,4 @@ +export const apiGetUserLogin = async () => { + const response = await fetch(`/api/new/user`) + return await response.json().catch(() => null) +} \ No newline at end of file diff --git a/src/app_modules/user/lib/type_user.ts b/src/app_modules/user/lib/type_user.ts new file mode 100644 index 00000000..a0f63da4 --- /dev/null +++ b/src/app_modules/user/lib/type_user.ts @@ -0,0 +1,14 @@ +export interface IUserLogin { + id: string + username: string + nomor: string + active:boolean + masterUserRoleId: string + idProfile: string + name: string + email: string + alamat: string + jenisKelamin: string + imageId: string + imageBackgroundId: string +} \ No newline at end of file diff --git a/src/middleware.ts b/src/middleware.ts index d08b310f..621bde58 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -32,6 +32,7 @@ const middlewareConfig: MiddlewareConfig = { "/auth/api/login", "/aset/global/main_background.png", "/aset/logo/logo-hipmi.png", + "/api/new/*" ], encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, sessionKey: process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, From 66c65bc937089fb0f3bcf09f32e239790d704be3 Mon Sep 17 00:00:00 2001 From: amel Date: Thu, 5 Dec 2024 17:21:59 +0800 Subject: [PATCH 021/595] upd: profile dan katalog Deskripsi: - mecah dan ganti ke api No Issues --- src/app/api/new/portofolio/route.ts | 55 +++++++ src/app/api/new/user/route.ts | 61 ++++---- src/app/dev/katalog/[id]/page.tsx | 8 +- src/app_modules/katalog/index.ts | 2 + src/app_modules/katalog/portofolio/index.ts | 4 + .../katalog/portofolio/lib/api_portofolio.ts | 4 + .../katalog/portofolio/lib/type_portofolio.ts | 6 + .../katalog/ui/list_portolio_new.tsx | 139 ++++++++++++++++++ src/app_modules/katalog/ui/profile_detail.tsx | 131 +++++++++++++++++ .../katalog/ui/skeleton_profile.tsx | 42 ++++++ src/app_modules/katalog/view_katalog_new.tsx | 20 +++ src/app_modules/user/index.ts | 8 +- src/app_modules/user/lib/api_user.ts | 4 +- src/app_modules/user/lib/type_user.ts | 2 +- 14 files changed, 444 insertions(+), 42 deletions(-) create mode 100644 src/app/api/new/portofolio/route.ts create mode 100644 src/app_modules/katalog/portofolio/lib/api_portofolio.ts create mode 100644 src/app_modules/katalog/portofolio/lib/type_portofolio.ts create mode 100644 src/app_modules/katalog/ui/list_portolio_new.tsx create mode 100644 src/app_modules/katalog/ui/profile_detail.tsx create mode 100644 src/app_modules/katalog/ui/skeleton_profile.tsx create mode 100644 src/app_modules/katalog/view_katalog_new.tsx diff --git a/src/app/api/new/portofolio/route.ts b/src/app/api/new/portofolio/route.ts new file mode 100644 index 00000000..357f42b3 --- /dev/null +++ b/src/app/api/new/portofolio/route.ts @@ -0,0 +1,55 @@ +import { prisma } from "@/app/lib"; +import { NextResponse } from "next/server"; + + +// GET ALL DATA PORTOFOLIO BY PROFILE ID +export async function GET(request: Request) { + try { + let fixData + const { searchParams } = new URL(request.url) + const profile = searchParams.get("profile") + const page = searchParams.get("page") + + if (page == "profile") { + fixData = await prisma.portofolio.findMany({ + take: 2, + orderBy: { + createdAt: "desc", + }, + where: { + profileId: profile, + active: true, + }, + select: { + id: true, + id_Portofolio: true, + namaBisnis: true, + profileId: true, + }, + }); + } else if (page == "portofolio") { + fixData = await prisma.portofolio.findMany({ + orderBy: { + createdAt: "desc", + }, + where: { + profileId: profile, + active: true, + }, + select: { + id: true, + id_Portofolio: true, + namaBisnis: true, + profileId: true, + }, + }); + } + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: fixData }, { 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/user/route.ts b/src/app/api/new/user/route.ts index 8a343879..73a4fb52 100644 --- a/src/app/api/new/user/route.ts +++ b/src/app/api/new/user/route.ts @@ -1,53 +1,50 @@ import { prisma } from "@/app/lib"; -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; import { NextResponse } from "next/server"; -// GET DATA USER LOGIN +// GET ONE DATA USER PROFILE BY PROFILE ID export async function GET(request: Request) { try { - const userLoginId = await funGetUserIdByToken() - if (userLoginId == null) { - return NextResponse.json({ success: false, message: "Gagal mendapatkan data, user id tidak ada" }, { status: 500 }); - } + const { searchParams } = new URL(request.url) + const profile = searchParams.get("profile") - const data = await prisma.user.findFirst({ + const data = await prisma.profile.findUnique({ where: { - id: userLoginId, + id: String(profile), }, select: { id: true, - username: true, - nomor: true, - active: true, - masterUserRoleId: true, - Profile: { + name: true, + email: true, + alamat: true, + jenisKelamin: true, + imageId: true, + imageBackgroundId: true, + userId: true, + User: { select: { - id: true, - name: true, - email: true, - alamat: true, - jenisKelamin: true, - imageId: true, - imageBackgroundId: true + username: true, + nomor: true, + active: true, + masterUserRoleId: true } } } }); const dataFix = { - id: data?.id, - username: data?.username, - nomor: data?.nomor, - active: data?.active, - masterUserRoleId: data?.masterUserRoleId, - idProfile: data?.Profile?.id, - name: data?.Profile?.name, - email: data?.Profile?.email, - alamat: data?.Profile?.alamat, - jenisKelamin: data?.Profile?.jenisKelamin, - imageId: data?.Profile?.imageId, - imageBackgroundId: data?.Profile?.imageBackgroundId + id: data?.userId, + username: data?.User?.username, + nomor: data?.User?.nomor, + active: data?.User?.active, + masterUserRoleId: data?.User?.masterUserRoleId, + idProfile: data?.id, + name: data?.name, + email: data?.email, + alamat: data?.alamat, + jenisKelamin: data?.jenisKelamin, + imageId: data?.imageId, + imageBackgroundId: data?.imageBackgroundId } diff --git a/src/app/dev/katalog/[id]/page.tsx b/src/app/dev/katalog/[id]/page.tsx index 24cccdab..e37c8933 100644 --- a/src/app/dev/katalog/[id]/page.tsx +++ b/src/app/dev/katalog/[id]/page.tsx @@ -1,5 +1,5 @@ import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import { Katalog_MainView } from "@/app_modules/katalog"; +import { Katalog_MainView, ViewKatalogNew } from "@/app_modules/katalog"; import { funGetListPortofolio } from "@/app_modules/katalog/portofolio/fun/get/get_list_portofolio"; import { Profile_getOneProfileAndUserById } from "@/app_modules/katalog/profile/fun/get/get_one_user_profile"; @@ -12,11 +12,13 @@ export default async function Page({ params }: { params: { id: string } }) { return ( <> - + /> */} + + ); } diff --git a/src/app_modules/katalog/index.ts b/src/app_modules/katalog/index.ts index 9892f2b6..165c1ec3 100644 --- a/src/app_modules/katalog/index.ts +++ b/src/app_modules/katalog/index.ts @@ -1,2 +1,4 @@ +import ViewKatalogNew from "./view_katalog_new"; export { Katalog_MainView } from "./view_katalog"; +export { ViewKatalogNew } diff --git a/src/app_modules/katalog/portofolio/index.ts b/src/app_modules/katalog/portofolio/index.ts index 062117f7..b45526dc 100644 --- a/src/app_modules/katalog/portofolio/index.ts +++ b/src/app_modules/katalog/portofolio/index.ts @@ -1,3 +1,5 @@ +import { IListPortofolio } from './lib/type_portofolio'; +import { apiGetPortofolioByProfile } from './lib/api_portofolio'; import CreatePortofolio from "./create/view"; import CreatePortofolioLayout from "./create/layout"; import PortofolioLayout from "./ui/ui_layout"; @@ -20,6 +22,8 @@ export { LayoutPortofolio_EditDataBisnis, LayoutPortofolio_EditLogoBisnis, LayoutPortofolio_EditMedsosBisnis, + apiGetPortofolioByProfile }; +export type { IListPortofolio }; export { Portofolio_ViewListDetail } from "./view/view_list_detail_portofolio"; diff --git a/src/app_modules/katalog/portofolio/lib/api_portofolio.ts b/src/app_modules/katalog/portofolio/lib/api_portofolio.ts new file mode 100644 index 00000000..a4c460db --- /dev/null +++ b/src/app_modules/katalog/portofolio/lib/api_portofolio.ts @@ -0,0 +1,4 @@ +export const apiGetPortofolioByProfile = async (path?: string) => { + const response = await fetch(`/api/new/portofolio${(path) ? path : ''}`) + return await response.json().catch(() => null) +} \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/lib/type_portofolio.ts b/src/app_modules/katalog/portofolio/lib/type_portofolio.ts new file mode 100644 index 00000000..637935e1 --- /dev/null +++ b/src/app_modules/katalog/portofolio/lib/type_portofolio.ts @@ -0,0 +1,6 @@ +export interface IListPortofolio { + id: string + id_Portofolio: string + profileId: string + namaBisnis: string +} \ No newline at end of file diff --git a/src/app_modules/katalog/ui/list_portolio_new.tsx b/src/app_modules/katalog/ui/list_portolio_new.tsx new file mode 100644 index 00000000..b1118bb3 --- /dev/null +++ b/src/app_modules/katalog/ui/list_portolio_new.tsx @@ -0,0 +1,139 @@ +import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import { Box, Center, Group, Paper, Skeleton, Stack, Text, Title } from "@mantine/core"; +import { apiGetPortofolioByProfile, IListPortofolio } from "../portofolio"; +import { useState } from "react"; +import { useParams, useRouter } from "next/navigation"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { RouterPortofolio } from "@/app/lib/router_hipmi/router_katalog"; +import { IconCaretRight } from "@tabler/icons-react"; + +export default function ListPortofolioProfileNew() { + const router = useRouter(); + const param = useParams<{ id: string }>() + const [loading, setLoading] = useState(true) + const [dataPortofolio, setDataPortofolio] = useState([]) + + async function getPortofolio() { + try { + setLoading(true) + const response = await apiGetPortofolioByProfile(`?profile=${param.id}&page=profile`) + if (response.success) { + setDataPortofolio(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getPortofolio() + }, []); + + + return ( + <> + + + + Portofolio + + + + { + loading ? + <> + + + + : + + _.isEmpty(dataPortofolio) ? ( +
+ + - Belum Ada Portofolio - + +
+ ) : ( + + {dataPortofolio.map((e, i) => ( + { + router.push(RouterPortofolio.main_detail + e?.id); + }} + style={{ + backgroundColor: MainColor.darkblue, + border: `2px solid ${AccentColor.blue}`, + borderRadius: "10px ", + padding: "15px", + color: "white", + }} + > + + + + {e?.namaBisnis} + + + #{e.id_Portofolio} + + + + + + + + ))} + + ) + } + + { + loading ? <> + : + _.isEmpty(dataPortofolio) ? ( + "" + ) : ( + + + router.push( + RouterPortofolio.daftar_portofolio + param.id, + { scroll: false } + ) + } + fw={"bold"} + fz={"sm"} + > + Lihat semua + + + ) + } +
+
+
+ + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/ui/profile_detail.tsx b/src/app_modules/katalog/ui/profile_detail.tsx new file mode 100644 index 00000000..e5c4ca20 --- /dev/null +++ b/src/app_modules/katalog/ui/profile_detail.tsx @@ -0,0 +1,131 @@ +import { AccentColor } from "@/app_modules/_global/color"; +import { apiGetUserProfile, IUserProfile } from "@/app_modules/user"; +import { Box, Center, Group, Stack, Text, ThemeIcon } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconBrandGmail, IconGenderFemale, IconGenderMale, IconHome, IconPhone } from "@tabler/icons-react"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import { Profile_ComponentAvatarProfile, Profile_ComponentLoadBackgroundImage } from "../profile/_component"; +import SkeletonProfile from "./skeleton_profile"; + +export default function ProfileDetail() { + const param = useParams<{ id: string }>() + const [loading, setLoading] = useState(true) + const [dataProfile, setDataProfile] = useState() + const listInformation = [ + { + icon: , + value: "+" + dataProfile?.nomor, + }, + { + icon: , + value: dataProfile?.email, + }, + { + icon: , + value: dataProfile?.alamat, + }, + { + icon: + dataProfile?.jenisKelamin === "Laki-laki" ? ( + + ) : ( + + ), + value: dataProfile?.jenisKelamin, + }, + ]; + + + async function getProfile() { + try { + setLoading(true) + const response = await apiGetUserProfile(`?profile=${param.id}`) + if (response.success) { + setDataProfile(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getProfile() + }, []); + + return <> + + { + loading ? + : + <> + + + + +
+ +
+ + + {dataProfile?.name} + + + @{dataProfile?.username} + + +
+
+ + + {listInformation.map((e, i) => ( + + + {e.icon} + + + {e?.value} + + + ))} + + + + } +
+ ; +} \ No newline at end of file diff --git a/src/app_modules/katalog/ui/skeleton_profile.tsx b/src/app_modules/katalog/ui/skeleton_profile.tsx new file mode 100644 index 00000000..bc64b0da --- /dev/null +++ b/src/app_modules/katalog/ui/skeleton_profile.tsx @@ -0,0 +1,42 @@ +import { Avatar, Box, Center, Grid, Skeleton, Stack } from "@mantine/core"; + +export default function SkeletonProfile() { + return ( + <> + + + +
+ +
+
+ + + + + + + {[...Array(4)].map((_, index) => ( + + + + + + + + + + + ))} + +
+ + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/view_katalog_new.tsx b/src/app_modules/katalog/view_katalog_new.tsx new file mode 100644 index 00000000..1ce3c013 --- /dev/null +++ b/src/app_modules/katalog/view_katalog_new.tsx @@ -0,0 +1,20 @@ +'use client' +import { Stack } from "@mantine/core"; +import ProfileDetail from "./ui/profile_detail"; +import ListPortofolioProfileNew from "./ui/list_portolio_new"; + +export default function ViewKatalogNew() { + return ( + <> + + + {/* */} + + + + ) +} \ No newline at end of file diff --git a/src/app_modules/user/index.ts b/src/app_modules/user/index.ts index 343e274c..8779b5f7 100644 --- a/src/app_modules/user/index.ts +++ b/src/app_modules/user/index.ts @@ -1,5 +1,5 @@ -import { IUserLogin } from './lib/type_user'; -import { apiGetUserLogin } from "./lib/api_user"; +import { IUserProfile } from './lib/type_user'; +import { apiGetUserProfile } from "./lib/api_user"; -export { apiGetUserLogin }; -export type { IUserLogin }; +export { apiGetUserProfile }; +export type { IUserProfile }; diff --git a/src/app_modules/user/lib/api_user.ts b/src/app_modules/user/lib/api_user.ts index 8db7415b..7fca1371 100644 --- a/src/app_modules/user/lib/api_user.ts +++ b/src/app_modules/user/lib/api_user.ts @@ -1,4 +1,4 @@ -export const apiGetUserLogin = async () => { - const response = await fetch(`/api/new/user`) +export const apiGetUserProfile = async (path?: string) => { + const response = await fetch(`/api/new/user${(path) ? path : ''}`) return await response.json().catch(() => null) } \ No newline at end of file diff --git a/src/app_modules/user/lib/type_user.ts b/src/app_modules/user/lib/type_user.ts index a0f63da4..2f5a2177 100644 --- a/src/app_modules/user/lib/type_user.ts +++ b/src/app_modules/user/lib/type_user.ts @@ -1,4 +1,4 @@ -export interface IUserLogin { +export interface IUserProfile { id: string username: string nomor: string From 057df80c316fe6385e407536b71cbd91115b5fc3 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Fri, 6 Dec 2024 09:08:57 +0800 Subject: [PATCH 022/595] Fix event Deskripsi: - Fix konfirmasi - Fix qr code --- next.config.js | 13 +- src/app/api/event/check-kehadiran/route.ts | 2 +- src/app/api/event/get-all-by-userId/route.ts | 9 - src/app/api/event/get-all/route.ts | 12 + src/app/api/event/get-one-by-id/route.ts | 21 + src/app/dev/event/create/page.tsx | 4 +- src/app/dev/event/konfirmasi/[id]/page.tsx | 45 +- src/app/dev/event/main/beranda/page.tsx | 4 +- .../lib/api_user_router/route_api_event.ts | 7 + .../admin/event/_view/view_table_review.tsx | 12 +- src/app_modules/event/_ui/konfirmasi.tsx | 396 ++++++++++++++---- .../component/card_view/card_beranda.tsx | 6 +- src/app_modules/event/component/index.ts | 2 + .../skeleton/comp_skeleton_beranda.tsx | 29 ++ src/app_modules/event/main/beranda.tsx | 31 +- src/app_modules/event/splash/index.tsx | 2 +- 16 files changed, 436 insertions(+), 159 deletions(-) delete mode 100644 src/app/api/event/get-all-by-userId/route.ts create mode 100644 src/app/api/event/get-all/route.ts create mode 100644 src/app/api/event/get-one-by-id/route.ts create mode 100644 src/app/lib/api_user_router/route_api_event.ts create mode 100644 src/app_modules/event/component/skeleton/comp_skeleton_beranda.tsx diff --git a/next.config.js b/next.config.js index 5c728631..79d67970 100644 --- a/next.config.js +++ b/next.config.js @@ -1,10 +1,9 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: false, - experimental: { - serverActions: true - }, - -} + reactStrictMode: false, + experimental: { + serverActions: true, + }, +}; -module.exports = nextConfig +module.exports = nextConfig; diff --git a/src/app/api/event/check-kehadiran/route.ts b/src/app/api/event/check-kehadiran/route.ts index 3b2ef875..083f6e05 100644 --- a/src/app/api/event/check-kehadiran/route.ts +++ b/src/app/api/event/check-kehadiran/route.ts @@ -11,5 +11,5 @@ export async function GET(req: Request) { userId: userId as string, }); - return NextResponse.json({ res }); + return NextResponse.json(res, { status: 200 }); } diff --git a/src/app/api/event/get-all-by-userId/route.ts b/src/app/api/event/get-all-by-userId/route.ts deleted file mode 100644 index 2ae65ce8..00000000 --- a/src/app/api/event/get-all-by-userId/route.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { NextResponse } from "next/server"; - -export async function GET(params: Request) { - const { searchParams } = new URL(params.url); - const userId = searchParams.get("userId"); - - - return NextResponse.json({ userId }); -} \ No newline at end of file diff --git a/src/app/api/event/get-all/route.ts b/src/app/api/event/get-all/route.ts new file mode 100644 index 00000000..33e9c89a --- /dev/null +++ b/src/app/api/event/get-all/route.ts @@ -0,0 +1,12 @@ +import { event_getListAllPublish } from "@/app_modules/event/fun/get/get_list_all_publish"; +import { toNumber } from "lodash"; +import { NextResponse } from "next/server"; + +export async function GET(params: Request) { + const { searchParams } = new URL(params.url); + const page = searchParams.get("page"); + + const data = await event_getListAllPublish({ page: toNumber(page) }); + + return NextResponse.json({ data }); +} diff --git a/src/app/api/event/get-one-by-id/route.ts b/src/app/api/event/get-one-by-id/route.ts new file mode 100644 index 00000000..7ab66fac --- /dev/null +++ b/src/app/api/event/get-one-by-id/route.ts @@ -0,0 +1,21 @@ +import { event_getOneById } from "@/app_modules/event/fun/get/get_one_by_id"; +import { NextResponse } from "next/server"; + +export async function GET(params: Request) { + const { searchParams } = new URL(params.url); + const eventId = searchParams.get("eventId"); + + const res = await event_getOneById(eventId as string); + + if (!res) { + return NextResponse.json( + { message: "Event Not Found", data: null }, + { status: 404 } + ); + } + + return NextResponse.json( + { message: "Event Found", data: res }, + { status: 200 } + ); +} diff --git a/src/app/dev/event/create/page.tsx b/src/app/dev/event/create/page.tsx index 5363ccb1..b731e460 100644 --- a/src/app/dev/event/create/page.tsx +++ b/src/app/dev/event/create/page.tsx @@ -1,9 +1,9 @@ -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import { newFunGetUserId } from "@/app/lib/new_fun_user_id"; import { Event_Create } from "@/app_modules/event"; import { Event_getMasterTipeAcara } from "@/app_modules/event/fun/master/get_tipe_acara"; export default async function Page() { - const userLoginId = await funGetUserIdByToken(); + const userLoginId = await newFunGetUserId(); const listTipeAcara = await Event_getMasterTipeAcara(); return ( diff --git a/src/app/dev/event/konfirmasi/[id]/page.tsx b/src/app/dev/event/konfirmasi/[id]/page.tsx index eb04ea33..05703cb1 100644 --- a/src/app/dev/event/konfirmasi/[id]/page.tsx +++ b/src/app/dev/event/konfirmasi/[id]/page.tsx @@ -1,33 +1,35 @@ import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; import Ui_Konfirmasi from "@/app_modules/event/_ui/konfirmasi"; -import { - event_funCheckKehadiran, - event_funCheckPesertaByUserId, -} from "@/app_modules/event/fun"; +import { event_funCheckPesertaByUserId } from "@/app_modules/event/fun"; import { event_getOneById } from "@/app_modules/event/fun/get/get_one_by_id"; import moment from "moment"; import { redirect } from "next/navigation"; -export default async function Page({ params }: { params: { id: string } }) { - const eventId = params.id; - const userLoginId = await funGetUserIdByToken(); - const dataEvent = await event_getOneById(eventId); +export default async function Page({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const eventId = (await params).id; - const checkPeserta = await event_funCheckPesertaByUserId({ - eventId: eventId, - userId: userLoginId as string, - }); - if (dataEvent == null) return redirect("/dev/event/main/beranda"); + // const userLoginId = await funGetUserIdByToken(); - if (moment(dataEvent?.tanggal).diff(moment(), "minutes") > 0) - return redirect("/dev/event/main/beranda"); + // const checkPeserta = await event_funCheckPesertaByUserId({ + // eventId: eventId, + // userId: userLoginId as string, + // }); - if (dataEvent?.isArsip) - return redirect(`/dev/event/detail/riwayat/${dataEvent.id}`); + // if (dataEvent == null) return redirect("/dev/event/main/beranda"); - if (checkPeserta == false) - return redirect(`/dev/event/detail/main/${eventId}`); + // if (moment(dataEvent?.tanggal).diff(moment(), "minutes") > 0) + // return redirect("/dev/event/main/beranda"); + + // if (dataEvent?.isArsip) + // return redirect(`/dev/event/detail/riwayat/${dataEvent.id}`); + + // if (checkPeserta == false) + // return redirect(`/dev/event/detail/main/${eventId}`); // if (checkKehadiran) { // return redirect(`/dev/event/main/beranda`); @@ -35,10 +37,7 @@ export default async function Page({ params }: { params: { id: string } }) { return ( <> - + ); } diff --git a/src/app/dev/event/main/beranda/page.tsx b/src/app/dev/event/main/beranda/page.tsx index e5201847..5bde2a88 100644 --- a/src/app/dev/event/main/beranda/page.tsx +++ b/src/app/dev/event/main/beranda/page.tsx @@ -1,12 +1,10 @@ import { Event_Beranda } from "@/app_modules/event"; -import { event_getListAllPublish } from "@/app_modules/event/fun/get/get_list_all_publish"; export default async function Page() { - const dataEvent = await event_getListAllPublish({ page: 1 }); return ( <> - + ); } diff --git a/src/app/lib/api_user_router/route_api_event.ts b/src/app/lib/api_user_router/route_api_event.ts new file mode 100644 index 00000000..1394e4ec --- /dev/null +++ b/src/app/lib/api_user_router/route_api_event.ts @@ -0,0 +1,7 @@ +export const API_RouteEvent = { + get_all: ({ page }: { page: number }) => `/api/event/get-all?page=${page}`, + get_one_by_id: ({ eventId }: { eventId: string }) => + `/api/event/get-one-by-id?eventId=${eventId}`, + check_kehadiran: ({ eventId, userId }: { eventId: string; userId: string }) => + `/api/event/check-kehadiran?eventId=${eventId}&userId=${userId}`, +}; diff --git a/src/app_modules/admin/event/_view/view_table_review.tsx b/src/app_modules/admin/event/_view/view_table_review.tsx index 4b9ab12c..4b64fb13 100644 --- a/src/app_modules/admin/event/_view/view_table_review.tsx +++ b/src/app_modules/admin/event/_view/view_table_review.tsx @@ -108,15 +108,15 @@ export default function AdminEvent_ComponentTableReview({ async function onPublish({ eventId, - tanggalSelesai, + tanggal, }: { eventId: string; - tanggalSelesai: Date; + tanggal: Date; }) { const checkStatus = await event_checkStatus({ id: eventId }); if (checkStatus) { - if (moment(tanggalSelesai).diff(Date.now(), "minutes") < 0) + if (moment(tanggal).diff(Date.now(), "minutes") < 0) return ComponentGlobal_NotifikasiPeringatan( "Waktu acara telah lewat, Report untuk memberitahu user !" ); @@ -267,7 +267,7 @@ export default function AdminEvent_ComponentTableReview({ - +
@@ -286,7 +286,7 @@ export default function AdminEvent_ComponentTableReview({ onClick={() => onPublish({ eventId: e.id, - tanggalSelesai: e.tanggalSelesai, + tanggal: e.tanggal, }) } > @@ -297,7 +297,7 @@ export default function AdminEvent_ComponentTableReview({ leftIcon={} radius={"xl"} onClick={async () => { - const checkStatus = await event_checkStatus({ id: eventId }); + const checkStatus = await event_checkStatus({ id: e.id }); if (checkStatus) { open(); diff --git a/src/app_modules/event/_ui/konfirmasi.tsx b/src/app_modules/event/_ui/konfirmasi.tsx index ec95fc00..90194d65 100644 --- a/src/app_modules/event/_ui/konfirmasi.tsx +++ b/src/app_modules/event/_ui/konfirmasi.tsx @@ -4,7 +4,17 @@ import { UIGlobal_LayoutDefault, UIGlobal_LayoutTamplate, } from "@/app_modules/_global/ui"; -import { Button, Paper, Skeleton, Stack, Text, Title } from "@mantine/core"; +import { + Button, + Card, + Center, + Group, + Paper, + Skeleton, + Stack, + Text, + Title, +} from "@mantine/core"; import { MODEL_EVENT } from "../model/interface"; import { useShallowEffect } from "@mantine/hooks"; import { AccentColor, MainColor } from "@/app_modules/_global/color"; @@ -13,112 +23,328 @@ import { ComponentGlobal_NotifikasiBerhasil, ComponentGlobal_NotifikasiGagal, } from "@/app_modules/_global/notif_global"; -import { useRouter } from "next/navigation"; +import { redirect, useRouter } from "next/navigation"; import { RouterEvent } from "@/app/lib/router_hipmi/router_event"; import { useState } from "react"; +import { API_RouteEvent } from "@/app/lib/api_user_router/route_api_event"; +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import moment from "moment"; +import { gs_event_hotMenu } from "../global_state"; +import { useAtom } from "jotai"; export default function Ui_Konfirmasi({ - dataEvent, userLoginId, + eventId, }: { - dataEvent: MODEL_EVENT; userLoginId: string; + eventId: string; }) { // console.log(dataEvent); const router = useRouter(); - const [isLoading, setLoading] = useState(false); + const [data, setData] = useState(null); const [isPresent, setIsPresent] = useState(null); useShallowEffect(() => { - onLoadKehadiran({ - onChange(val) { - setIsPresent(val); - }, - }); - }, [setIsPresent]); + onLoadData(); + }, []); - async function onLoadKehadiran({ - onChange, - }: { - onChange: (val: boolean) => void; - }) { - const res = await fetch( - `/api/event/check-kehadiran?userId=${userLoginId}&eventId=${dataEvent.id}` + async function onLoadData() { + const data = await fetch( + API_RouteEvent.get_one_by_id({ eventId: eventId }) ); - const checkKehadiran = await res.json(); - - onChange(checkKehadiran.res); - } - async function onUpdateKonfirmasi() { - setLoading(true); - const res = await event_funUpdateKehadiran({ - eventId: dataEvent.id, - userId: userLoginId, - }); - - if (res.status === 200) { - ComponentGlobal_NotifikasiBerhasil(res.message, 2000); - router.push(RouterEvent.detail_main + dataEvent.id); - } else { - setLoading(false); - ComponentGlobal_NotifikasiGagal(res.message); - } + const res = await data.json(); + console.log(res.data, "data event"); + setData(res.data); } - console.log(isPresent, "isPresent"); + useShallowEffect(() => { + onLoadKehadiran(); + }, []); + + async function onLoadKehadiran() { + const res = await fetch( + API_RouteEvent.check_kehadiran({ eventId: eventId, userId: userLoginId }) + ); + const data = await res.json(); + + setIsPresent(data); + } + + // console.log("kehadiran:", isPresent); + // console.log("data:", data); + + if (data == null && isPresent == null) { + return ; + } + + if (data == null) { + return ( + <> + + + ); + } + + if (moment(data?.tanggal).diff(moment(), "minute") < 0) { + return ( + <> + + + ); + } + + if (isPresent && data) { + return ; + } + + if (isPresent == false && data) { + return ( + + ); + } + // const tgl = moment(data?.tanggal).diff(moment(), "minute") < 0; + // return ( + // <> + // + // + // {JSON.stringify(tgl)} + // + // + // + // ); +} + +function DataNotFound() { + const router = useRouter(); + const [isLoading, setLoading] = useState(false); + const [hotMenu, setHotMenu] = useAtom(gs_event_hotMenu); return ( <> - - {isPresent == null ? ( - - ) : isPresent ? ( - - - - Anda telah terkonfirmasi silahkan kembali ke beranda ! - - {dataEvent.title} - - - - ) : ( - - - - Anda mengkonfirmasi bahwa anda telah datang & ikut menghadir - di event - - {dataEvent.title} - - - - )} + + + + + Data Event Tidak Ditemukan + + + + + + + + + ); +} + +function SkeletonIsDataNull() { + return ( + <> + + + + + {" "} + {" "} + +
+ +
+
+
+
+
+ + ); +} + +function EventAlreadyDone({ + title, + eventId, +}: { + title: string; + eventId: string; +}) { + const router = useRouter(); + const [isLoading, setLoading] = useState(false); + const [isLoadingDetail, setLoadingDetail] = useState(false); + const [hotMenu, setHotMenu] = useAtom(gs_event_hotMenu); + + return ( + <> + + + + + + Kami mohon maaf, Bapak/Ibu, acara{" "} + + {title} + {" "} + telah selesai, sehingga konfirmasi kehadiran sudah tidak dapat + dilakukan. Terima kasih atas perhatian dan minat Anda. Kami + berharap dapat bertemu di acara kami berikutnya. Terima kasih, + Bapak/Ibu, kehadiran Anda di acara. + + + + + + + + + + + + ); +} + +function UserNotConfirm({ + title, + eventId, + userLoginId, +}: { + title: string; + eventId: string; + userLoginId: string; +}) { + const router = useRouter(); + const [isLoading, setLoading] = useState(false); + async function onUpdateKonfirmasi() { + setLoading(true); + const res = await event_funUpdateKehadiran({ + eventId: eventId, + userId: userLoginId, + }); + + if (res.status === 200) { + ComponentGlobal_NotifikasiBerhasil(res.message, 2000); + router.push(RouterEvent.detail_main + eventId); + } else { + setLoading(false); + ComponentGlobal_NotifikasiGagal(res.message); + } + } + return ( + <> + + + + + + Terima kasih atas kehadiran Anda di acara{" "} + + {title} + {" "} + pada hari ini. Mohon untuk mengonfirmasi kehadiran Anda dengan + menekan tombol {"Hadir"} atau fitur konfirmasi yang tersedia di + bawah. Terima kasih dan selamat menikmati acara. + + + + + + + + + ); +} + +function UserAlreadyConfirm({ title }: { title: string }) { + const router = useRouter(); + const [isLoading, setLoading] = useState(false); + const [hotMenu, setHotMenu] = useAtom(gs_event_hotMenu); + + return ( + <> + + + + + + Terima kasih, Bapak/Ibu, kehadiran Anda di acara{" "} + + {title} + {" "} + telah berhasil dikonfirmasi. Kami senang menyambut Anda dan + semoga acara ini memberikan manfaat yang maksimal. Selamat + mengikuti kegiatan. + + + + + diff --git a/src/app_modules/event/component/card_view/card_beranda.tsx b/src/app_modules/event/component/card_view/card_beranda.tsx index d751c207..adb952fa 100644 --- a/src/app_modules/event/component/card_view/card_beranda.tsx +++ b/src/app_modules/event/component/card_view/card_beranda.tsx @@ -34,11 +34,11 @@ export function ComponentEvent_CardBeranda({ data }: { data: any }) { {data.title} - + {/* {new Intl.DateTimeFormat("id-ID", { dateStyle: "medium", - }).format(data.tanggal)} - + }).format(data?.tanggal)} + */} diff --git a/src/app_modules/event/component/index.ts b/src/app_modules/event/component/index.ts index d9cc4eeb..b146da16 100644 --- a/src/app_modules/event/component/index.ts +++ b/src/app_modules/event/component/index.ts @@ -1,5 +1,7 @@ import Event_ComponentCreateButton from "./button/button_create_event"; +import Event_ComponentSkeletonBeranda from "./skeleton/comp_skeleton_beranda"; import { Event_ComponentSkeletonDetailData } from "./skeleton/comp_skeleton_detail_data"; export { Event_ComponentSkeletonDetailData }; export { Event_ComponentCreateButton }; +export { Event_ComponentSkeletonBeranda }; diff --git a/src/app_modules/event/component/skeleton/comp_skeleton_beranda.tsx b/src/app_modules/event/component/skeleton/comp_skeleton_beranda.tsx new file mode 100644 index 00000000..e45a9247 --- /dev/null +++ b/src/app_modules/event/component/skeleton/comp_skeleton_beranda.tsx @@ -0,0 +1,29 @@ +import { AccentColor } from "@/app_modules/_global/color"; +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Grid, Group, Paper, Skeleton, Stack, Text } from "@mantine/core"; + +export default function Event_ComponentSkeletonBeranda() { + return ( + <> + {Array.from({ length: 10 }).map((_, index) => ( + + + + + + + + + + + + + + + + + + ))} + + ); +} diff --git a/src/app_modules/event/main/beranda.tsx b/src/app_modules/event/main/beranda.tsx index 3387a29b..33565488 100644 --- a/src/app_modules/event/main/beranda.tsx +++ b/src/app_modules/event/main/beranda.tsx @@ -13,6 +13,7 @@ import { Loader, rem, Skeleton, + Paper, } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { useAtom } from "jotai"; @@ -22,12 +23,10 @@ import { useState } from "react"; import { ComponentEvent_CardBeranda } from "../component/card_view/card_beranda"; import { event_getListAllPublish } from "../fun/get/get_list_all_publish"; import { MODEL_EVENT } from "../model/interface"; +import { Event_ComponentSkeletonBeranda } from "../component"; +import { API_RouteEvent } from "@/app/lib/api_user_router/route_api_event"; -export default function Event_Beranda({ - dataEvent, -}: { - dataEvent: MODEL_EVENT[]; -}) { +export default function Event_Beranda() { const [data, setData] = useState(null); const [activePage, setActivePage] = useState(1); const [isLoading, setIsLoading] = useState(false); @@ -39,6 +38,7 @@ export default function Event_Beranda({ const [isShowUpdate, setIsShowUpdate] = useState(false); useShallowEffect(() => { + setIsShowUpdate(false); loadData(); }, []); @@ -49,14 +49,16 @@ export default function Event_Beranda({ }, [isTriggerEventBeranda]); async function loadData() { - const loadData = await event_getListAllPublish({ page: 1 }); - setData(loadData as any); + const res = await fetch(API_RouteEvent.get_all({ page: activePage })); + const data = await res.json(); + setData(data.data as any); } async function onLoadNewData() { setIsLoading(true); - const loadData = await event_getListAllPublish({ page: 1 }); - setData(loadData as any); + const res = await fetch(API_RouteEvent.get_all({ page: 1 })); + const data = await res.json(); + setData(data.data as any); setIsShowUpdate(false); setIsTriggerEventBeranca(false); @@ -92,16 +94,7 @@ export default function Event_Beranda({ {data == null ? ( - Array.from({ length: 10 }).map((_, index) => ( - - )) + ) : _.isEmpty(data) ? ( ) : ( diff --git a/src/app_modules/event/splash/index.tsx b/src/app_modules/event/splash/index.tsx index e6d75e20..bd06d4ca 100644 --- a/src/app_modules/event/splash/index.tsx +++ b/src/app_modules/event/splash/index.tsx @@ -16,7 +16,7 @@ export default function Event_SplashScreen() { setTimeout(() => { router.replace(RouterEvent.beranda); setHotMenu(0); - }, 1000); + }, 500); }, []); return ( <> From ea491231a8ec9e3a1db9523e029b07f4ba53ffd6 Mon Sep 17 00:00:00 2001 From: amel Date: Fri, 6 Dec 2024 11:44:48 +0800 Subject: [PATCH 023/595] upd: kataloh Deskripsi: - api di katalog No Issues --- src/app/dev/katalog/[id]/layout.tsx | 27 ++--- src/app/dev/katalog/[id]/page.tsx | 13 +-- .../katalog/component/drawer_katalog_new.tsx | 110 ++++++++++++++++++ src/app_modules/katalog/main/index.ts | 2 + src/app_modules/katalog/main/layout_new.tsx | 60 ++++++++++ src/app_modules/katalog/view_katalog_new.tsx | 5 - 6 files changed, 187 insertions(+), 30 deletions(-) create mode 100644 src/app_modules/katalog/component/drawer_katalog_new.tsx create mode 100644 src/app_modules/katalog/main/layout_new.tsx diff --git a/src/app/dev/katalog/[id]/layout.tsx b/src/app/dev/katalog/[id]/layout.tsx index bd874c57..98b718f6 100644 --- a/src/app/dev/katalog/[id]/layout.tsx +++ b/src/app/dev/katalog/[id]/layout.tsx @@ -1,31 +1,24 @@ -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import { KatalogLayout } from "@/app_modules/katalog/main"; -import { Profile_getOneProfileAndUserById } from "@/app_modules/katalog/profile/fun/get/get_one_user_profile"; +import { LayoutKatalogNew } from "@/app_modules/katalog/main"; -export default async function Layout({ - children, - params, -}: { - children: any; - params: { id: string }; -}) { - const profileId = params.id; - const dataProfile = await Profile_getOneProfileAndUserById(profileId); - const authorId = dataProfile?.userId; +export default async function Layout({ children, params, }: { children: any; params: { id: string } }) { + // const profileId = params.id; + // const dataProfile = await Profile_getOneProfileAndUserById(profileId); + // const authorId = dataProfile?.userId; - const userLoginId = await funGetUserIdByToken(); - const userRoleId = dataProfile?.User?.masterUserRoleId; + // const userLoginId = await funGetUserIdByToken(); + // const userRoleId = dataProfile?.User?.masterUserRoleId; return ( <> - {children} - + */} + {children} ); } diff --git a/src/app/dev/katalog/[id]/page.tsx b/src/app/dev/katalog/[id]/page.tsx index e37c8933..98ab33e3 100644 --- a/src/app/dev/katalog/[id]/page.tsx +++ b/src/app/dev/katalog/[id]/page.tsx @@ -1,14 +1,11 @@ -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import { Katalog_MainView, ViewKatalogNew } from "@/app_modules/katalog"; -import { funGetListPortofolio } from "@/app_modules/katalog/portofolio/fun/get/get_list_portofolio"; -import { Profile_getOneProfileAndUserById } from "@/app_modules/katalog/profile/fun/get/get_one_user_profile"; +import { ViewKatalogNew } from "@/app_modules/katalog"; export default async function Page({ params }: { params: { id: string } }) { - let profileId = params.id; - const userLoginId = await funGetUserIdByToken(); + // let profileId = params.id; + // const userLoginId = await funGetUserIdByToken(); - const listPorto = await funGetListPortofolio(profileId); - const dataProfile = await Profile_getOneProfileAndUserById(profileId); + // const listPorto = await funGetListPortofolio(profileId); + // const dataProfile = await Profile_getOneProfileAndUserById(profileId); return ( <> diff --git a/src/app_modules/katalog/component/drawer_katalog_new.tsx b/src/app_modules/katalog/component/drawer_katalog_new.tsx new file mode 100644 index 00000000..c0ef291f --- /dev/null +++ b/src/app_modules/katalog/component/drawer_katalog_new.tsx @@ -0,0 +1,110 @@ +import { RouterAdminDashboard } from "@/app/lib/router_hipmi/router_admin"; +import { RouterPortofolio, RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; +import { AccentColor } from "@/app_modules/_global/color"; +import Component_ButtonLogout from "@/app_modules/auth/logout/view"; +import { ActionIcon, Drawer, Group, SimpleGrid, Stack, Text } from "@mantine/core"; +import { IconDashboard } from "@tabler/icons-react"; +import { IconEdit, IconPencilPlus, IconPhotoEdit, IconPolaroid, IconX } from "@tabler/icons-react"; +import { useParams, useRouter } from "next/navigation"; + +export default function DrawerKatalogNew({ opened, close, userRoleId, userId }: { opened: boolean, close: () => void, userRoleId: string, userId: string }) { + const param = useParams<{ id: string }>() + const router = useRouter() + + const listPage = [ + { + id: "1", + name: "Edit profile", + icon: , + path: RouterProfile.edit + param.id, + }, + { + id: "2", + name: "Ubah foto profile", + icon: , + path: RouterProfile.update_foto_profile + param.id, + }, + { + id: "3", + name: "Ubah latar belakang", + icon: , + path: RouterProfile.update_foto_background + param.id, + }, + { + id: "4", + name: "Tambah portofolio", + icon: , + path: RouterPortofolio.create + param.id, + }, + ]; + + return <> + close()} + position={"bottom"} + size={"auto"} + withCloseButton={false} + styles={{ + content: { + padding: 0, + position: "absolute", + margin: "auto", + backgroundColor: "transparent", + left: 0, + right: 0, + width: 500, + }, + body: { + backgroundColor: AccentColor.darkblue, + borderTop: `2px solid ${AccentColor.blue}`, + borderRight: `1px solid ${AccentColor.blue}`, + borderLeft: `1px solid ${AccentColor.blue}`, + borderRadius: "20px 20px 0px 0px", + color: "white", + paddingBottom: "5%", + }, + }} + > + + + + + + + + {listPage.map((e, i) => ( + + { router.push(e.path, { scroll: false }); }} + > + {e.icon} + + + {e.name} + + + ))} + + + {userRoleId != "1" && ( + + { router.push(RouterAdminDashboard.main_admin, { scroll: false }); }} + > + + + + Dashboard Admin + + + )} + + + + ; +} \ No newline at end of file diff --git a/src/app_modules/katalog/main/index.ts b/src/app_modules/katalog/main/index.ts index 793e49ee..127d7b75 100644 --- a/src/app_modules/katalog/main/index.ts +++ b/src/app_modules/katalog/main/index.ts @@ -1,3 +1,5 @@ import KatalogLayout from "./layout"; +import LayoutKatalogNew from "./layout_new"; export { KatalogLayout }; +export { LayoutKatalogNew } diff --git a/src/app_modules/katalog/main/layout_new.tsx b/src/app_modules/katalog/main/layout_new.tsx new file mode 100644 index 00000000..a0aa4d8e --- /dev/null +++ b/src/app_modules/katalog/main/layout_new.tsx @@ -0,0 +1,60 @@ +'use client' +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import { UIGlobal_LayoutHeaderTamplate, UIGlobal_LayoutTamplate } from "@/app_modules/_global/ui"; +import { apiGetUserProfile } from "@/app_modules/user"; +import { ActionIcon } from "@mantine/core"; +import { useDisclosure, useShallowEffect } from "@mantine/hooks"; +import { IconDotsVertical } from "@tabler/icons-react"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import DrawerKatalogNew from "../component/drawer_katalog_new"; + +export default function LayoutKatalogNew({ children }: { children: any }) { + const param = useParams<{ id: string }>() + const [authorId, setAuthorId] = useState("") + const [userRoleId, setUserRoleId] = useState("") + const [userLoginId, setUserLoginId] = useState("") + const [opened, { open, close }] = useDisclosure() + + async function getProfile() { + try { + const response = await apiGetUserProfile(`?profile=${param.id}`) + const response2 = await funGetUserIdByToken() + if (response.success) { + setAuthorId(response.data.id) + setUserRoleId(response.data.masterUserRoleId) + setUserLoginId(response2) + } + } catch (error) { + console.error(error); + } + } + + useShallowEffect(() => { + getProfile() + }, []) + + return ( + <> + + ) : ( + open()}> + + + ) + } + /> + } + > + {children} + close()} userRoleId={userRoleId} userId={userLoginId} /> + + + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/view_katalog_new.tsx b/src/app_modules/katalog/view_katalog_new.tsx index 1ce3c013..108e2c93 100644 --- a/src/app_modules/katalog/view_katalog_new.tsx +++ b/src/app_modules/katalog/view_katalog_new.tsx @@ -8,11 +8,6 @@ export default function ViewKatalogNew() { <> - {/* */} From dc8e30ae8763b551399262155851db983e4e719f Mon Sep 17 00:00:00 2001 From: amel Date: Fri, 6 Dec 2024 12:19:42 +0800 Subject: [PATCH 024/595] upd: kataloh deskripsi: - update api list portofolio No Issues --- src/app/api/new/portofolio/route.ts | 8 ++- .../daftar-portofolio/[id]/page.tsx | 16 ++--- src/app_modules/katalog/portofolio/index.ts | 4 +- .../ui/ui_list_detail_portofolio_new.tsx | 59 +++++++++++++++++++ .../view/list_detail_portofolio_new.tsx | 13 ++++ .../katalog/ui/list_portolio_new.tsx | 2 +- 6 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 src/app_modules/katalog/portofolio/ui/ui_list_detail_portofolio_new.tsx create mode 100644 src/app_modules/katalog/portofolio/view/list_detail_portofolio_new.tsx diff --git a/src/app/api/new/portofolio/route.ts b/src/app/api/new/portofolio/route.ts index 357f42b3..b25478d5 100644 --- a/src/app/api/new/portofolio/route.ts +++ b/src/app/api/new/portofolio/route.ts @@ -8,9 +8,11 @@ export async function GET(request: Request) { let fixData const { searchParams } = new URL(request.url) const profile = searchParams.get("profile") + const kategori_halaman = searchParams.get("cat") const page = searchParams.get("page") + const dataSkip = Number(page) * 10 - 10; - if (page == "profile") { + if (kategori_halaman == "profile") { fixData = await prisma.portofolio.findMany({ take: 2, orderBy: { @@ -27,8 +29,10 @@ export async function GET(request: Request) { profileId: true, }, }); - } else if (page == "portofolio") { + } else if (kategori_halaman == "portofolio") { fixData = await prisma.portofolio.findMany({ + skip: dataSkip, + take: 10, orderBy: { createdAt: "desc", }, diff --git a/src/app/dev/portofolio/daftar-portofolio/[id]/page.tsx b/src/app/dev/portofolio/daftar-portofolio/[id]/page.tsx index 45a30f23..2252757b 100644 --- a/src/app/dev/portofolio/daftar-portofolio/[id]/page.tsx +++ b/src/app/dev/portofolio/daftar-portofolio/[id]/page.tsx @@ -1,17 +1,17 @@ -import { Portofolio_ViewListDetail } from "@/app_modules/katalog/portofolio"; -import { portofolio_funGetAllDaftarByid } from "@/app_modules/katalog/portofolio/fun/get/get_all_portofolio"; +import { ListDetailPortofolioNew } from "@/app_modules/katalog/portofolio"; export default async function Page({ params }: { params: { id: string } }) { - const profileId = params.id; - const dataPortofolio = await portofolio_funGetAllDaftarByid({ - profileId, - page: 1, - }); + // const profileId = params.id; + // const dataPortofolio = await portofolio_funGetAllDaftarByid({ + // profileId, + // page: 1, + // }); return ( <> - + {/* */} + ); } diff --git a/src/app_modules/katalog/portofolio/index.ts b/src/app_modules/katalog/portofolio/index.ts index b45526dc..9d31be0f 100644 --- a/src/app_modules/katalog/portofolio/index.ts +++ b/src/app_modules/katalog/portofolio/index.ts @@ -10,6 +10,7 @@ import Portofolio_EditMedsosBisnis from "./edit/medsos/ui_edit_medsos"; import LayoutPortofolio_EditDataBisnis from "./edit/data/layout"; import LayoutPortofolio_EditLogoBisnis from "./edit/logo/layout"; import LayoutPortofolio_EditMedsosBisnis from "./edit/medsos/layout"; +import ListDetailPortofolioNew from './view/list_detail_portofolio_new'; export { CreatePortofolio, @@ -22,8 +23,9 @@ export { LayoutPortofolio_EditDataBisnis, LayoutPortofolio_EditLogoBisnis, LayoutPortofolio_EditMedsosBisnis, - apiGetPortofolioByProfile + apiGetPortofolioByProfile, }; export type { IListPortofolio }; export { Portofolio_ViewListDetail } from "./view/view_list_detail_portofolio"; +export { ListDetailPortofolioNew } diff --git a/src/app_modules/katalog/portofolio/ui/ui_list_detail_portofolio_new.tsx b/src/app_modules/katalog/portofolio/ui/ui_list_detail_portofolio_new.tsx new file mode 100644 index 00000000..9add314b --- /dev/null +++ b/src/app_modules/katalog/portofolio/ui/ui_list_detail_portofolio_new.tsx @@ -0,0 +1,59 @@ +import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; +import { Box, Center } from "@mantine/core"; +import { ScrollOnly } from "next-scroll-loader"; +import { useState } from "react"; +import { ComponentPortofolio_DaftarBoxView } from "../component/card_view_daftar"; +import { portofolio_funGetAllDaftarByid } from "../fun/get/get_all_portofolio"; +import { MODEL_PORTOFOLIO } from "../model/interface"; +import { useParams } from "next/navigation"; +import { useShallowEffect } from "@mantine/hooks"; +import { apiGetPortofolioByProfile } from "../lib/api_portofolio"; + +export default function Portofolio_UiListDetailNew() { + const param = useParams<{ id: string }>() + const profileId = param.id + const [data, setData] = useState([]) + const [activePage, setActivePage] = useState(1) + + async function getPortofolio() { + try { + const response = await apiGetPortofolioByProfile(`?profile=${param.id}&cat=portofolio&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } + } + + + useShallowEffect(() => { + getPortofolio() + }, []); + + return <> + + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const loadData = await portofolio_funGetAllDaftarByid({ + profileId, + page: activePage + 1, + }); + setActivePage((val) => val + 1); + + return loadData; + }} + > + {(item) => } +
+
+ ; +} \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/view/list_detail_portofolio_new.tsx b/src/app_modules/katalog/portofolio/view/list_detail_portofolio_new.tsx new file mode 100644 index 00000000..1920461d --- /dev/null +++ b/src/app_modules/katalog/portofolio/view/list_detail_portofolio_new.tsx @@ -0,0 +1,13 @@ +'use client' +import { UIGlobal_LayoutHeaderTamplate, UIGlobal_LayoutTamplate } from "@/app_modules/_global/ui"; +import Portofolio_UiListDetailNew from "../ui/ui_list_detail_portofolio_new"; + +export default function ListDetailPortofolioNew() { + return ( + <> + } > + + + + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/ui/list_portolio_new.tsx b/src/app_modules/katalog/ui/list_portolio_new.tsx index b1118bb3..ff7d96a4 100644 --- a/src/app_modules/katalog/ui/list_portolio_new.tsx +++ b/src/app_modules/katalog/ui/list_portolio_new.tsx @@ -17,7 +17,7 @@ export default function ListPortofolioProfileNew() { async function getPortofolio() { try { setLoading(true) - const response = await apiGetPortofolioByProfile(`?profile=${param.id}&page=profile`) + const response = await apiGetPortofolioByProfile(`?profile=${param.id}&cat=profile`) if (response.success) { setDataPortofolio(response.data); } From 72cd56deb89eb231213ce63a2add7aefe421af68 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Fri, 6 Dec 2024 13:51:04 +0800 Subject: [PATCH 025/595] chore(release): 1.2.19 --- CHANGELOG.md | 2 ++ package.json | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 108876d8..aa2c2693 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.19](https://github.com/bipproduction/hipmi/compare/v1.2.18...v1.2.19) (2024-12-06) + ## [1.2.18](https://github.com/bipproduction/hipmi/compare/v1.2.17...v1.2.18) (2024-12-04) ## [1.2.17](https://github.com/bipproduction/hipmi/compare/v1.2.16...v1.2.17) (2024-12-04) diff --git a/package.json b/package.json index bb7c07b7..33896d0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.18", + "version": "1.2.19", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" @@ -95,4 +95,4 @@ "wibu-pkg": "^1.0.3", "yaml": "^2.3.2" } -} \ No newline at end of file +} From 2b08326bed8098f2f416f652f904b6640c38df3a Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Fri, 6 Dec 2024 13:51:39 +0800 Subject: [PATCH 026/595] Fix event Deskripsi: - Fix user server menjadi API di beranda --- next.config.js | 1 + src/app/api/event/check-peserta/route.ts | 15 ++ src/app/api/event/list-peserta/route.ts | 16 ++ .../dev/event/detail/kontribusi/[id]/page.tsx | 3 +- src/app/dev/event/detail/main/[id]/page.tsx | 15 +- .../dev/event/detail/publish/[id]/page.tsx | 3 +- .../dev/event/detail/riwayat/[id]/page.tsx | 8 +- src/app/dev/event/konfirmasi/[id]/page.tsx | 27 +--- .../lib/api_user_router/route_api_event.ts | 3 + src/app_modules/event/_ui/konfirmasi.tsx | 103 +++++++++++- .../event/component/detail/detail_main.tsx | 148 +++++++++--------- .../event/component/detail/list_peserta.tsx | 96 +++++++----- src/app_modules/event/component/index.ts | 4 + .../skeleton/comp_skeleton_detail.tsx | 36 +++++ .../skeleton/comp_skeleton_list_peserta.tsx | 32 ++++ .../event/detail/kontribusi/index.tsx | 14 +- .../event/detail/main_detail/index.tsx | 62 +++++--- .../event/detail/publish/index.tsx | 9 +- .../event/detail/riwayat/index.tsx | 48 +----- .../event/fun/create/fun_join_and_confirm.ts | 23 +++ .../event/fun/get/new_get_list_peserta.ts | 43 +++++ src/app_modules/event/fun/index.ts | 2 + 22 files changed, 476 insertions(+), 235 deletions(-) create mode 100644 src/app/api/event/check-peserta/route.ts create mode 100644 src/app/api/event/list-peserta/route.ts create mode 100644 src/app_modules/event/component/skeleton/comp_skeleton_detail.tsx create mode 100644 src/app_modules/event/component/skeleton/comp_skeleton_list_peserta.tsx create mode 100644 src/app_modules/event/fun/create/fun_join_and_confirm.ts create mode 100644 src/app_modules/event/fun/get/new_get_list_peserta.ts diff --git a/next.config.js b/next.config.js index 79d67970..48384e99 100644 --- a/next.config.js +++ b/next.config.js @@ -4,6 +4,7 @@ const nextConfig = { experimental: { serverActions: true, }, + output: "standalone" }; module.exports = nextConfig; diff --git a/src/app/api/event/check-peserta/route.ts b/src/app/api/event/check-peserta/route.ts new file mode 100644 index 00000000..49b863e2 --- /dev/null +++ b/src/app/api/event/check-peserta/route.ts @@ -0,0 +1,15 @@ +import { event_funCheckPesertaByUserId } from "@/app_modules/event/fun"; +import { NextResponse } from "next/server"; + +export async function GET(req: Request) { + const { searchParams } = new URL(req.url); + const userId = searchParams.get("userId"); + const eventId = searchParams.get("eventId"); + + const res = await event_funCheckPesertaByUserId({ + eventId: eventId as string, + userId: userId as string, + }); + + return NextResponse.json(res, { status: 200 }); +} diff --git a/src/app/api/event/list-peserta/route.ts b/src/app/api/event/list-peserta/route.ts new file mode 100644 index 00000000..be0037a6 --- /dev/null +++ b/src/app/api/event/list-peserta/route.ts @@ -0,0 +1,16 @@ +import { event_newGetListPesertaById } from "@/app_modules/event/fun"; +import { toNumber } from "lodash"; +import { NextResponse } from "next/server"; + +export async function GET(req: Request) { + const { searchParams } = new URL(req.url); + const eventId = searchParams.get("eventId"); + const page = searchParams.get("page"); + + const res = await event_newGetListPesertaById({ + eventId: eventId as string, + page: toNumber(page), + }); + + return NextResponse.json(res, { status: 200 }); +} diff --git a/src/app/dev/event/detail/kontribusi/[id]/page.tsx b/src/app/dev/event/detail/kontribusi/[id]/page.tsx index 5678dc7a..508e14ae 100644 --- a/src/app/dev/event/detail/kontribusi/[id]/page.tsx +++ b/src/app/dev/event/detail/kontribusi/[id]/page.tsx @@ -11,8 +11,7 @@ export default async function Page({ params }: { params: { id: string } }) { return ( <> diff --git a/src/app/dev/event/detail/main/[id]/page.tsx b/src/app/dev/event/detail/main/[id]/page.tsx index 9f42a181..11850c4e 100644 --- a/src/app/dev/event/detail/main/[id]/page.tsx +++ b/src/app/dev/event/detail/main/[id]/page.tsx @@ -1,27 +1,18 @@ -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import { newFunGetUserId } from "@/app/lib/new_fun_user_id"; import { Event_DetailMain } from "@/app_modules/event"; import { Event_countTotalPesertaById } from "@/app_modules/event/fun/count/count_total_peserta_by_id"; -import { Event_CekUserJoinById } from "@/app_modules/event/fun/get/cek_user_join_by_id"; -import { Event_getListPesertaById } from "@/app_modules/event/fun/get/get_list_peserta_by_id"; -import { event_getOneById } from "@/app_modules/event/fun/get/get_one_by_id"; export default async function Page({ params }: { params: { id: string } }) { let eventId = params.id; - const userLoginId : any= await funGetUserIdByToken(); - - const dataEvent = await event_getOneById(eventId); - const listPeserta = await Event_getListPesertaById(eventId); - const isJoin = await Event_CekUserJoinById(eventId, userLoginId); + const userLoginId = await newFunGetUserId(); const totalPeserta = await Event_countTotalPesertaById(eventId); return ( <> ); diff --git a/src/app/dev/event/detail/publish/[id]/page.tsx b/src/app/dev/event/detail/publish/[id]/page.tsx index 4388c255..1123ede9 100644 --- a/src/app/dev/event/detail/publish/[id]/page.tsx +++ b/src/app/dev/event/detail/publish/[id]/page.tsx @@ -6,15 +6,14 @@ import { event_getOneById } from "@/app_modules/event/fun/get/get_one_by_id"; export default async function Page({ params }: { params: { id: string } }) { let eventId = params.id; const dataEvent = await event_getOneById(eventId); - const listPeserta = await Event_getListPesertaById(eventId); const totalPeserta = await Event_countTotalPesertaById(eventId); return ( ); } diff --git a/src/app/dev/event/detail/riwayat/[id]/page.tsx b/src/app/dev/event/detail/riwayat/[id]/page.tsx index 2f506396..a6715a97 100644 --- a/src/app/dev/event/detail/riwayat/[id]/page.tsx +++ b/src/app/dev/event/detail/riwayat/[id]/page.tsx @@ -1,21 +1,15 @@ - import { Event_DetailRiwayat } from "@/app_modules/event"; import { Event_countTotalPesertaById } from "@/app_modules/event/fun/count/count_total_peserta_by_id"; -import { Event_getListPesertaById } from "@/app_modules/event/fun/get/get_list_peserta_by_id"; -import { event_getOneById } from "@/app_modules/event/fun/get/get_one_by_id"; export default async function Page({ params }: { params: { id: string } }) { let eventId = params.id; - const dataEvent = await event_getOneById(eventId); - const listPeserta = await Event_getListPesertaById(eventId); const totalPeserta = await Event_countTotalPesertaById(eventId); return ( <> ); diff --git a/src/app/dev/event/konfirmasi/[id]/page.tsx b/src/app/dev/event/konfirmasi/[id]/page.tsx index 05703cb1..3a0af55b 100644 --- a/src/app/dev/event/konfirmasi/[id]/page.tsx +++ b/src/app/dev/event/konfirmasi/[id]/page.tsx @@ -1,3 +1,4 @@ +import { newFunGetUserId } from "@/app/lib/new_fun_user_id"; import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; import Ui_Konfirmasi from "@/app_modules/event/_ui/konfirmasi"; import { event_funCheckPesertaByUserId } from "@/app_modules/event/fun"; @@ -11,33 +12,11 @@ export default async function Page({ params: Promise<{ id: string }>; }) { const eventId = (await params).id; - - - // const userLoginId = await funGetUserIdByToken(); - - // const checkPeserta = await event_funCheckPesertaByUserId({ - // eventId: eventId, - // userId: userLoginId as string, - // }); - - // if (dataEvent == null) return redirect("/dev/event/main/beranda"); - - // if (moment(dataEvent?.tanggal).diff(moment(), "minutes") > 0) - // return redirect("/dev/event/main/beranda"); - - // if (dataEvent?.isArsip) - // return redirect(`/dev/event/detail/riwayat/${dataEvent.id}`); - - // if (checkPeserta == false) - // return redirect(`/dev/event/detail/main/${eventId}`); - - // if (checkKehadiran) { - // return redirect(`/dev/event/main/beranda`); - // } + const userLoginId = await newFunGetUserId(); return ( <> - + ); } diff --git a/src/app/lib/api_user_router/route_api_event.ts b/src/app/lib/api_user_router/route_api_event.ts index 1394e4ec..9b7094c2 100644 --- a/src/app/lib/api_user_router/route_api_event.ts +++ b/src/app/lib/api_user_router/route_api_event.ts @@ -4,4 +4,7 @@ export const API_RouteEvent = { `/api/event/get-one-by-id?eventId=${eventId}`, check_kehadiran: ({ eventId, userId }: { eventId: string; userId: string }) => `/api/event/check-kehadiran?eventId=${eventId}&userId=${userId}`, + check_peserta: ({ eventId, userId }: { eventId: string; userId: string }) => + `/api/event/check-peserta?eventId=${eventId}&userId=${userId}`, + list_peserta: ({ eventId, page }: { eventId: string, page: number }) => `/api/event/list-peserta?eventId=${eventId}&page=${page}`, }; diff --git a/src/app_modules/event/_ui/konfirmasi.tsx b/src/app_modules/event/_ui/konfirmasi.tsx index 90194d65..8b1814b7 100644 --- a/src/app_modules/event/_ui/konfirmasi.tsx +++ b/src/app_modules/event/_ui/konfirmasi.tsx @@ -31,6 +31,8 @@ import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; import moment from "moment"; import { gs_event_hotMenu } from "../global_state"; import { useAtom } from "jotai"; +import { Event_funJoinEvent } from "../fun/create/fun_join_event"; +import { Event_funJoinAndConfirmEvent } from "../fun/create/fun_join_and_confirm"; export default function Ui_Konfirmasi({ userLoginId, @@ -43,6 +45,7 @@ export default function Ui_Konfirmasi({ const router = useRouter(); const [data, setData] = useState(null); + const [isJoin, setIsJoin] = useState(null); const [isPresent, setIsPresent] = useState(null); useShallowEffect(() => { @@ -54,10 +57,24 @@ export default function Ui_Konfirmasi({ API_RouteEvent.get_one_by_id({ eventId: eventId }) ); const res = await data.json(); - console.log(res.data, "data event"); setData(res.data); } + // CEK PESERTA + useShallowEffect(() => { + onCheckPeserta(); + }, []); + + async function onCheckPeserta() { + const res = await fetch( + API_RouteEvent.check_peserta({ eventId: eventId, userId: userLoginId }) + ); + const data = await res.json(); + console.log("cek peserta", data); + setIsJoin(data); + } + + // CEK KEHADIRAN useShallowEffect(() => { onLoadKehadiran(); }, []); @@ -67,7 +84,6 @@ export default function Ui_Konfirmasi({ API_RouteEvent.check_kehadiran({ eventId: eventId, userId: userLoginId }) ); const data = await res.json(); - setIsPresent(data); } @@ -94,6 +110,18 @@ export default function Ui_Konfirmasi({ ); } + if (isJoin == false) { + return ( + <> + + + ); + } + if (isPresent && data) { return ; } @@ -177,6 +205,77 @@ function SkeletonIsDataNull() { ); } +function UserNotJoin({ + title, + eventId, + userLoginId, +}: { + title: string; + eventId: string; + userLoginId: string; +}) { + const router = useRouter(); + const [isLoading, setLoading] = useState(false); + async function onJoinAndKonfirmasi() { + setLoading(true); + + const body = { + eventId: eventId, + userId: userLoginId, + }; + + const res = await Event_funJoinAndConfirmEvent(body as any); + + if (res.status === 200) { + ComponentGlobal_NotifikasiBerhasil(res.message, 2000); + router.push(RouterEvent.detail_main + eventId); + } else { + setLoading(false); + ComponentGlobal_NotifikasiGagal(res.message); + } + } + return ( + <> + + + + + + Halo, Bapak/Ibu. Kami mencatat bahwa Anda belum melakukan + registrasi melalui aplikasi untuk mengikuti acara{" "} + + {title}. + {" "} + Mohon segera lakukan registrasi melalui Event App agar dapat + mengikuti acara ini. Jika membutuhkan bantuan, jangan ragu untuk + menghubungi tim kami. Terima kasih Terima kasih atas kehadiran + Anda di acara pada hari ini. Mohon untuk mengonfirmasi kehadiran + Anda dengan menekan tombol {"Join & Konfirmasi"} + atau fitur konfirmasi yang tersedia di bawah. Terima kasih dan + selamat menikmati acara. + + + + + + + + + ); +} + function EventAlreadyDone({ title, eventId, diff --git a/src/app_modules/event/component/detail/detail_main.tsx b/src/app_modules/event/component/detail/detail_main.tsx index bfcdefea..4625b1e6 100644 --- a/src/app_modules/event/component/detail/detail_main.tsx +++ b/src/app_modules/event/component/detail/detail_main.tsx @@ -4,104 +4,110 @@ import { ComponentGlobal_AvatarAndUsername, ComponentGlobal_CardStyles, } from "@/app_modules/_global/component"; -import { Grid, Stack, Text, Title } from "@mantine/core"; +import { Center, Grid, Skeleton, Stack, Text, Title } from "@mantine/core"; import { MODEL_EVENT } from "../../model/interface"; +import { useShallowEffect } from "@mantine/hooks"; +import { useState } from "react"; +import { API_RouteEvent } from "@/app/lib/api_user_router/route_api_event"; +import { Event_ComponentSkeletonDetail } from "../skeleton/comp_skeleton_detail"; +import moment from "moment"; +import "moment/locale/id"; export default function ComponentEvent_DetailMainData({ - data, + eventId, }: { - data: MODEL_EVENT; + eventId: string; }) { - const tgl = data.tanggal; - const hari = tgl.toLocaleString("id-ID", { dateStyle: "full" }); + const [data, setData] = useState(null); - const jam = tgl.toLocaleTimeString([], { - timeStyle: "short", - hourCycle: "h24", - }); + useShallowEffect(() => { + onLoadData(); + }, []); + + async function onLoadData() { + const data = await fetch( + API_RouteEvent.get_one_by_id({ eventId: eventId }) + ); + const res = await data.json(); + setData(res.data); + } return ( <> - - - + {data == null ? ( + + ) : ( + + + - - - {data ? data.title : null} - - - - Lokasi - - : - - {data ? data.lokasi : null} - - - - - Tipe Acara - - : - - {data ? data.EventMaster_TipeAcara.name : null} - - - - - Tanggal & Waktu + + + {data ? data.title : null} + - Mulai + Lokasi : - - {" "} - {new Intl.DateTimeFormat("id-ID", { - dateStyle: "full", - }).format(data?.tanggal)} - ,{" "} - - {new Intl.DateTimeFormat("id-ID", { - timeStyle: "short", - }).format(data?.tanggal)} - - + {data ? data.lokasi : null} - Selesai + Tipe Acara : - - {" "} - {new Intl.DateTimeFormat("id-ID", { - dateStyle: "full", - }).format(data?.tanggalSelesai)} - ,{" "} - - {new Intl.DateTimeFormat("id-ID", { - timeStyle: "short", - }).format(data?.tanggalSelesai)} - - + {data ? data.EventMaster_TipeAcara.name : null} - - - Deskripsi - {data ? data?.deskripsi : null} + + Tanggal & Waktu + + + Mulai + + : + + + {moment( + data.tanggal?.toLocaleString("id-ID", { + dateStyle: "full", + }) + ).format("dddd, DD MMMM YYYY, LT")} + + + + + + Selesai + + : + + + {moment( + data.tanggalSelesai?.toLocaleString("id-ID", { + dateStyle: "full", + }) + ).format("dddd, DD MMMM YYYY, LT")} + + + + + + + Deskripsi + {data ? data?.deskripsi : null} + - - + + )} ); } diff --git a/src/app_modules/event/component/detail/list_peserta.tsx b/src/app_modules/event/component/detail/list_peserta.tsx index 73d56a58..a8c53851 100644 --- a/src/app_modules/event/component/detail/list_peserta.tsx +++ b/src/app_modules/event/component/detail/list_peserta.tsx @@ -6,6 +6,7 @@ import { Center, Grid, Group, + Skeleton, Stack, Text, Title, @@ -26,55 +27,78 @@ import { funGlobal_CheckProfile } from "@/app_modules/_global/fun/get"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; import { useState } from "react"; import moment from "moment"; +import { useShallowEffect } from "@mantine/hooks"; +import { API_RouteEvent } from "@/app/lib/api_user_router/route_api_event"; +import Event_ComponentSkeletonListPeserta from "../skeleton/comp_skeleton_list_peserta"; export default function ComponentEvent_ListPeserta({ - listPeserta, total, + eventId, + isNewPeserta, }: { - listPeserta: MODEL_EVENT_PESERTA[]; total: number; + eventId: string; + isNewPeserta?: boolean | null; }) { const router = useRouter(); + const [data, setData] = useState(null); + + useShallowEffect(() => { + onLoadPeserta(); + }, []); + + useShallowEffect(() => { + if (isNewPeserta !== null && isNewPeserta === true) { + onLoadPeserta(); + } + }, [isNewPeserta]); + + async function onLoadPeserta() { + const res = await fetch( + API_RouteEvent.list_peserta({ eventId: eventId, page: 1 }) + ); + const data = await res.json(); + setData(data); + } + return ( <> - - -
- Daftar Peserta ({total}) -
- - {_.isEmpty(listPeserta) ? ( + {data === null ? ( + + ) : ( + +
- - - Tidak ada peserta - - + Daftar Peserta ({total})
- ) : ( - - {listPeserta.map((e, i) => ( - - {/* */} - + {_.isEmpty(data) ? ( +
+ + - Tidak ada peserta - + +
+ ) : ( + + {data.map((e, i) => ( + + - {/* */} - - ))} - - )} -
-
+ {/* */} +
+ ))} + + )} + +
+ )} ); } diff --git a/src/app_modules/event/component/index.ts b/src/app_modules/event/component/index.ts index b146da16..6180e8a6 100644 --- a/src/app_modules/event/component/index.ts +++ b/src/app_modules/event/component/index.ts @@ -1,7 +1,11 @@ import Event_ComponentCreateButton from "./button/button_create_event"; import Event_ComponentSkeletonBeranda from "./skeleton/comp_skeleton_beranda"; +import { Event_ComponentSkeletonDetail } from "./skeleton/comp_skeleton_detail"; import { Event_ComponentSkeletonDetailData } from "./skeleton/comp_skeleton_detail_data"; +import Event_ComponentSkeletonListPeserta from "./skeleton/comp_skeleton_list_peserta"; export { Event_ComponentSkeletonDetailData }; export { Event_ComponentCreateButton }; export { Event_ComponentSkeletonBeranda }; +export { Event_ComponentSkeletonDetail }; +export { Event_ComponentSkeletonListPeserta }; diff --git a/src/app_modules/event/component/skeleton/comp_skeleton_detail.tsx b/src/app_modules/event/component/skeleton/comp_skeleton_detail.tsx new file mode 100644 index 00000000..200491a5 --- /dev/null +++ b/src/app_modules/event/component/skeleton/comp_skeleton_detail.tsx @@ -0,0 +1,36 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Stack, Center, Skeleton, Grid } from "@mantine/core"; + +export function Event_ComponentSkeletonDetail() { + return ( + <> + + + + + + + + + + + + +
+ +
+ + + + + + + + + + +
+
+ + ); +} diff --git a/src/app_modules/event/component/skeleton/comp_skeleton_list_peserta.tsx b/src/app_modules/event/component/skeleton/comp_skeleton_list_peserta.tsx new file mode 100644 index 00000000..9cca2112 --- /dev/null +++ b/src/app_modules/event/component/skeleton/comp_skeleton_list_peserta.tsx @@ -0,0 +1,32 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Stack, Center, Skeleton, Grid } from "@mantine/core"; + +export default function Event_ComponentSkeletonListPeserta() { + return ( + <> + + +
+ +
+ + + {Array.from(new Array(3)).map((e, i) => ( + + + + + + + + + + + + ))} + +
+
+ + ); +} diff --git a/src/app_modules/event/detail/kontribusi/index.tsx b/src/app_modules/event/detail/kontribusi/index.tsx index ee709ea4..6d667ad1 100644 --- a/src/app_modules/event/detail/kontribusi/index.tsx +++ b/src/app_modules/event/detail/kontribusi/index.tsx @@ -3,25 +3,19 @@ import { Stack } from "@mantine/core"; import ComponentEvent_DetailMainData from "../../component/detail/detail_main"; import ComponentEvent_ListPeserta from "../../component/detail/list_peserta"; -import { MODEL_EVENT, MODEL_EVENT_PESERTA } from "../../model/interface"; export default function Event_DetailKontribusi({ - dataEvent, - listKontributor, + eventId, totalPeserta, }: { - dataEvent: MODEL_EVENT; - listKontributor: MODEL_EVENT_PESERTA[]; + eventId: string; totalPeserta: number; }) { return ( <> - - + + ); diff --git a/src/app_modules/event/detail/main_detail/index.tsx b/src/app_modules/event/detail/main_detail/index.tsx index 71376d11..3438ba1a 100644 --- a/src/app_modules/event/detail/main_detail/index.tsx +++ b/src/app_modules/event/detail/main_detail/index.tsx @@ -1,11 +1,12 @@ "use client"; +import { API_RouteEvent } from "@/app/lib/api_user_router/route_api_event"; import { IRealtimeData } from "@/app/lib/global_state"; import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; import notifikasiToUser_funCreate from "@/app_modules/notifikasi/fun/create/create_notif_to_user"; -import { Button, Stack } from "@mantine/core"; -import { useRouter } from "next/navigation"; +import { Button, Skeleton, Stack } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; import { useState } from "react"; import { WibuRealtime } from "wibu-pkg"; import ComponentEvent_DetailMainData from "../../component/detail/detail_main"; @@ -13,31 +14,43 @@ import ComponentEvent_ListPeserta from "../../component/detail/list_peserta"; import { Event_countTotalPesertaById } from "../../fun/count/count_total_peserta_by_id"; import { Event_funJoinEvent } from "../../fun/create/fun_join_event"; import { Event_getListPesertaById } from "../../fun/get/get_list_peserta_by_id"; -import { MODEL_EVENT, MODEL_EVENT_PESERTA } from "../../model/interface"; export default function Event_DetailMain({ - dataEvent, - listPeserta, userLoginId, - isJoin, totalPeserta, + eventId, }: { - dataEvent: MODEL_EVENT; - listPeserta: MODEL_EVENT_PESERTA[]; userLoginId: string; - isJoin: boolean; totalPeserta: number; + eventId: string; }) { - const router = useRouter(); const [total, setTotal] = useState(totalPeserta); - const [peserta, setPeserta] = useState(listPeserta); const [isLoading, setLoading] = useState(false); + const [isJoinSuccess, setIsJoinSuccess] = useState(null); + const [isNewPeserta, setIsNewPeserta] = useState(null); + + useShallowEffect(() => { + onCheckPeserta(); + }, []); + + async function onCheckPeserta() { + const res = await fetch( + API_RouteEvent.check_peserta({ eventId: eventId, userId: userLoginId }) + ); + const data = await res.json(); + setIsJoinSuccess(data); + } return ( <> - - {isJoin ? ( + + + {isJoinSuccess == null ? ( + + ) : isJoinSuccess ? ( @@ -50,10 +63,11 @@ export default function Event_DetailMain({ onClick={() => { onJoin( userLoginId, - dataEvent.id, - setPeserta, + eventId, setTotal, - setLoading + setLoading, + setIsJoinSuccess, + setIsNewPeserta ); }} > @@ -61,7 +75,11 @@ export default function Event_DetailMain({ )} - + ); @@ -70,9 +88,11 @@ export default function Event_DetailMain({ async function onJoin( userId: string, eventId: string, - setPeserta: any, setTotal: any, - setLoading: any + setLoading: any, + setIsJoinSuccess: (val: boolean | null) => void, + setIsNewPeserta: any + ) { const body = { userId: userId, @@ -84,7 +104,7 @@ async function onJoin( const res = await Event_funJoinEvent(body as any); if (res.status === 200) { const resPeserta = await Event_getListPesertaById(eventId); - setPeserta(resPeserta); + setIsNewPeserta(true); const resTotal = await Event_countTotalPesertaById(eventId); setTotal(resTotal); @@ -111,7 +131,7 @@ async function onJoin( }); } } - + setIsJoinSuccess(true); setLoading(true); ComponentGlobal_NotifikasiBerhasil(res.message, 2000); } else { diff --git a/src/app_modules/event/detail/publish/index.tsx b/src/app_modules/event/detail/publish/index.tsx index 9afb7e66..71df87a8 100644 --- a/src/app_modules/event/detail/publish/index.tsx +++ b/src/app_modules/event/detail/publish/index.tsx @@ -6,20 +6,17 @@ import { MODEL_EVENT } from "../../model/interface"; export default function Event_DetailPublish({ dataEvent, - listPeserta, totalPeserta, + eventId, }: { dataEvent: MODEL_EVENT; - listPeserta: any[]; totalPeserta: number; + eventId: string; }) { return ( <> - + ); } diff --git a/src/app_modules/event/detail/riwayat/index.tsx b/src/app_modules/event/detail/riwayat/index.tsx index 3d9ef6e9..774eced6 100644 --- a/src/app_modules/event/detail/riwayat/index.tsx +++ b/src/app_modules/event/detail/riwayat/index.tsx @@ -1,63 +1,27 @@ "use client"; -import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; -import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; -import { - Stack -} from "@mantine/core"; +import { Stack } from "@mantine/core"; import { useRouter } from "next/navigation"; import { useState } from "react"; import ComponentEvent_DetailMainData from "../../component/detail/detail_main"; import ComponentEvent_ListPeserta from "../../component/detail/list_peserta"; -import { Event_countTotalPesertaById } from "../../fun/count/count_total_peserta_by_id"; -import { Event_funJoinEvent } from "../../fun/create/fun_join_event"; -import { Event_getListPesertaById } from "../../fun/get/get_list_peserta_by_id"; -import { MODEL_EVENT, MODEL_EVENT_PESERTA } from "../../model/interface"; export default function Event_DetailRiwayat({ - dataEvent, - listPeserta, totalPeserta, + eventId, }: { - dataEvent: MODEL_EVENT; - listPeserta: MODEL_EVENT_PESERTA[]; totalPeserta: number; + eventId: string; }) { const router = useRouter(); const [total, setTotal] = useState(totalPeserta); - const [peserta, setPeserta] = useState(listPeserta); + return ( <> - - + + ); } - -async function onJoin( - userId: string, - eventId: string, - setPeserta: any, - setTotal: any -) { - const body = { - userId: userId, - eventId: eventId, - }; - - await Event_funJoinEvent(body as any).then(async (res) => { - if (res.status === 200) { - await Event_getListPesertaById(eventId).then(async (val) => { - await Event_countTotalPesertaById(eventId).then((ttl) => { - setPeserta(val); - setTotal(ttl); - ComponentGlobal_NotifikasiBerhasil(res.message, 2000); - }); - }); - } else { - ComponentGlobal_NotifikasiGagal(res.message); - } - }); -} diff --git a/src/app_modules/event/fun/create/fun_join_and_confirm.ts b/src/app_modules/event/fun/create/fun_join_and_confirm.ts new file mode 100644 index 00000000..6cee969c --- /dev/null +++ b/src/app_modules/event/fun/create/fun_join_and_confirm.ts @@ -0,0 +1,23 @@ +"use server"; + +import prisma from "@/app/lib/prisma"; +import { MODEL_EVENT_PESERTA } from "../../model/interface"; +import { revalidatePath } from "next/cache"; + +export async function Event_funJoinAndConfirmEvent(data: MODEL_EVENT_PESERTA) { + const join = await prisma.event_Peserta.create({ + data: { + eventId: data.eventId, + userId: data.userId, + isPresent: true, + }, + }); + + if (!join) return { status: 400, message: "Gagal Join & Konfirmasi" }; + + revalidatePath("/dev/event/detail/main"); + return { + status: 200, + message: "Berhasil Join & Konfirmasi", + }; +} diff --git a/src/app_modules/event/fun/get/new_get_list_peserta.ts b/src/app_modules/event/fun/get/new_get_list_peserta.ts new file mode 100644 index 00000000..cf44bf0c --- /dev/null +++ b/src/app_modules/event/fun/get/new_get_list_peserta.ts @@ -0,0 +1,43 @@ +"use server"; + +import prisma from "@/app/lib/prisma"; + +export async function event_newGetListPesertaById({ + eventId, + page, +}: { + eventId: string; + page: number; +}) { + const takeData = 10; + const skipData = page * takeData - takeData; + + const data = await prisma.event_Peserta.findMany({ + take: takeData, + skip: skipData, + orderBy: { + updatedAt: "desc", + }, + where: { + eventId: eventId, + }, + select: { + id: true, + active: true, + createdAt: true, + updatedAt: true, + userId: true, + + isPresent: true, + User: { + select: { + Profile: true, + }, + }, + Event: true, + eventId: true, + }, + }); + + return data; +} diff --git a/src/app_modules/event/fun/index.ts b/src/app_modules/event/fun/index.ts index 39702477..b63c8c35 100644 --- a/src/app_modules/event/fun/index.ts +++ b/src/app_modules/event/fun/index.ts @@ -1,6 +1,7 @@ import { event_funUpdateKehadiran } from "./edit/fun_update_konfirmasi_by_user_id"; import { event_funCheckKehadiran } from "./get/fun_check_kehadiran"; import { event_funCheckPesertaByUserId } from "./get/fun_check_peserta_by_user_id"; +import { event_newGetListPesertaById } from "./get/new_get_list_peserta"; import { event_getAllByStatusId } from "./get/status/get_all_by_status_id"; import { event_getMasterStatus } from "./master/get_status_event"; @@ -9,3 +10,4 @@ export { event_getMasterStatus }; export { event_funCheckPesertaByUserId }; export { event_funUpdateKehadiran }; export { event_funCheckKehadiran }; +export { event_newGetListPesertaById }; From 3cb5416cfb3fd8e7e3d9db47ef11fe1fc00f9df4 Mon Sep 17 00:00:00 2001 From: amel Date: Fri, 6 Dec 2024 21:33:24 +0800 Subject: [PATCH 027/595] upd: portofolio detail Deskripsi: - update api detail portofolio - delete api portofolio No Issues --- src/app/api/new/portofolio/[id]/route.ts | 172 ++++++++++++++++++ src/app/dev/portofolio/main/[id]/layout.tsx | 23 +-- src/app/dev/portofolio/main/[id]/page.tsx | 15 +- .../component/button_delete_new.tsx | 97 ++++++++++ .../portofolio/component/button_more_new.tsx | 87 +++++++++ src/app_modules/katalog/portofolio/index.ts | 4 + .../katalog/portofolio/lib/api_portofolio.ts | 15 ++ .../katalog/portofolio/lib/type_portofolio.ts | 29 +++ .../portofolio/ui/ui_detail_data_new.tsx | 131 +++++++++++++ .../portofolio/ui/ui_detail_map_new.tsx | 129 +++++++++++++ .../portofolio/ui/ui_detail_media_new.tsx | 131 +++++++++++++ .../ui/ui_detail_portofolio_new.tsx | 19 ++ .../katalog/portofolio/ui/ui_layout_new.tsx | 20 ++ .../ui/ui_skeleton_detail_bisnis.tsx | 35 ++++ 14 files changed, 884 insertions(+), 23 deletions(-) create mode 100644 src/app/api/new/portofolio/[id]/route.ts create mode 100644 src/app_modules/katalog/portofolio/component/button_delete_new.tsx create mode 100644 src/app_modules/katalog/portofolio/component/button_more_new.tsx create mode 100644 src/app_modules/katalog/portofolio/ui/ui_detail_data_new.tsx create mode 100644 src/app_modules/katalog/portofolio/ui/ui_detail_map_new.tsx create mode 100644 src/app_modules/katalog/portofolio/ui/ui_detail_media_new.tsx create mode 100644 src/app_modules/katalog/portofolio/ui/ui_detail_portofolio_new.tsx create mode 100644 src/app_modules/katalog/portofolio/ui/ui_layout_new.tsx create mode 100644 src/app_modules/katalog/portofolio/ui/ui_skeleton_detail_bisnis.tsx diff --git a/src/app/api/new/portofolio/[id]/route.ts b/src/app/api/new/portofolio/[id]/route.ts new file mode 100644 index 00000000..5d783cb5 --- /dev/null +++ b/src/app/api/new/portofolio/[id]/route.ts @@ -0,0 +1,172 @@ +import { prisma } from "@/app/lib"; +import { NextResponse } from "next/server"; +import fs from "fs"; + + + +// GET ONE DATA PORTOFOLIO BY ID PORTOFOLIO +export async function GET(request: Request, context: { params: { id: string } }) { + try { + let dataFix + const { id } = context.params; + const { searchParams } = new URL(request.url); + const kategori = searchParams.get('cat'); + + if (kategori == "bisnis") { + const data = await prisma.portofolio.findUnique({ + where: { + id: id, + }, + select: { + id_Portofolio: true, + namaBisnis: true, + alamatKantor: true, + tlpn: true, + deskripsi: true, + logoId: true, + MasterBidangBisnis: { + select: { + name: true + } + }, + Profile: { + select: { + userId: true + } + } + } + }); + + dataFix = { + id_Portofolio: data?.id_Portofolio, + namaBisnis: data?.namaBisnis, + alamatKantor: data?.alamatKantor, + tlpn: data?.tlpn, + deskripsi: data?.deskripsi, + logoId: data?.logoId, + bidangBisnis: data?.MasterBidangBisnis?.name, + authorId: data?.Profile?.userId + } + + } else if (kategori == "lokasi") { + const data = await prisma.portofolio.findUnique({ + where: { + id: id, + }, + select: { + logoId: true, + BusinessMaps: { + select: { + id: true, + namePin: true, + latitude: true, + longitude: true, + imageId: true, + pinId: true + } + } + } + }); + + dataFix = { + mapId: data?.BusinessMaps?.id, + logoId: data?.logoId, + namePin: data?.BusinessMaps?.namePin, + latitude: data?.BusinessMaps?.latitude, + longitude: data?.BusinessMaps?.longitude, + imageId: data?.BusinessMaps?.imageId, + pinId: data?.BusinessMaps?.pinId + } + + } else if (kategori == "sosmed") { + const data = await prisma.portofolio.findUnique({ + where: { + id: id, + }, + select: { + Portofolio_MediaSosial: { + select: { + facebook: true, + twitter: true, + instagram: true, + tiktok: true, + youtube: true + } + } + } + }); + + dataFix = { + facebook: data?.Portofolio_MediaSosial?.facebook, + twitter: data?.Portofolio_MediaSosial?.twitter, + instagram: data?.Portofolio_MediaSosial?.instagram, + tiktok: data?.Portofolio_MediaSosial?.tiktok, + youtube: data?.Portofolio_MediaSosial?.youtube + } + } + + 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 (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} + +// DELETE ONE DATA PORTOFOLIO +export async function DELETE(request: Request, context: { params: { id: string } }) { + try { + const { id } = context.params + + const data = await prisma.portofolio.findUnique({ + where: { + id: id + } + }) + + const findLogo = await prisma.images.findFirst({ + where: { + id: String(data?.logoId), + }, + select: { + id: true, + url: true, + }, + }); + + if (findLogo) { + fs.unlinkSync(`./public/portofolio/logo/${findLogo.url}`) + const deleteLogo = await prisma.images.delete({ + where: { + id: String(findLogo?.id), + }, + }); + } + + + + const deletePortoMedsos = await prisma.portofolio_MediaSosial.delete({ + where: { + portofolioId: id, + }, + }); + + const deleteMap = await prisma.businessMaps.delete({ + where: { + portofolioId: id + } + }) + + const deletePortofolio = await prisma.portofolio.delete({ + where: { + id: id, + }, + }); + + return NextResponse.json({ success: true, message: "Berhasil menghapus data" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal menghapus data, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/dev/portofolio/main/[id]/layout.tsx b/src/app/dev/portofolio/main/[id]/layout.tsx index 42f4bc01..1be53c84 100644 --- a/src/app/dev/portofolio/main/[id]/layout.tsx +++ b/src/app/dev/portofolio/main/[id]/layout.tsx @@ -1,27 +1,20 @@ -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import { PortofolioLayout } from "@/app_modules/katalog/portofolio"; -import { portofolio_getOneById } from "@/app_modules/katalog/portofolio/fun/get/get_one_portofolio"; +import { PortofolioLayoutNew } from "@/app_modules/katalog/portofolio"; -export default async function Layout({ - children, - params, -}: { - children: any; - params: { id: string }; -}) { - let portoId = params.id; - const getPorto = await portofolio_getOneById(portoId); - const userLoginId = await funGetUserIdByToken(); +export default async function Layout({ children, params, }: { children: any; params: { id: string }; }) { + // let portoId = params.id; + // const getPorto = await portofolio_getOneById(portoId); + // const userLoginId = await funGetUserIdByToken(); return ( <> - {children} - + */} + {children} ); } diff --git a/src/app/dev/portofolio/main/[id]/page.tsx b/src/app/dev/portofolio/main/[id]/page.tsx index 61d37c14..ab45a811 100644 --- a/src/app/dev/portofolio/main/[id]/page.tsx +++ b/src/app/dev/portofolio/main/[id]/page.tsx @@ -1,20 +1,19 @@ -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import { ViewPortofolio } from "@/app_modules/katalog/portofolio"; -import { portofolio_getOneById } from "@/app_modules/katalog/portofolio/fun/get/get_one_portofolio"; +import { Portofolio_UiDetailNew } from "@/app_modules/katalog/portofolio"; const mapboxToken = process.env.MAPBOX_TOKEN!; export default async function Page({ params }: { params: { id: string } }) { - const portofolioId = params.id; - const dataPortofolio = await portofolio_getOneById(portofolioId); - const userLoginId = await funGetUserIdByToken(); + // const portofolioId = params.id; + // const dataPortofolio = await portofolio_getOneById(portofolioId); + // const userLoginId = await funGetUserIdByToken(); return ( <> - + /> */} + ); } diff --git a/src/app_modules/katalog/portofolio/component/button_delete_new.tsx b/src/app_modules/katalog/portofolio/component/button_delete_new.tsx new file mode 100644 index 00000000..84d890d2 --- /dev/null +++ b/src/app_modules/katalog/portofolio/component/button_delete_new.tsx @@ -0,0 +1,97 @@ +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import { ComponentGlobal_NotifikasiBerhasil, ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global"; +import { UIGlobal_Modal } from "@/app_modules/_global/ui"; +import { Button } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconTrash } from "@tabler/icons-react"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; +import { apiDeletePortofolio, apiGetOnePortofolioById } from "../lib/api_portofolio"; +import { IDetailPortofolioBisnis } from "../lib/type_portofolio"; + +export default function ComponentPortofolio_ButtonDeleteNew() { + const param = useParams<{ id: string }>() + const [openModal, setModal] = useState(false) + const [loadingDel, setLoadingDel] = useState(false) + const [userLoginId, setUserLoginId] = useState("") + const [dataPorto, setDataPorto] = useState() + const router = useRouter() + + + async function onDelete() { + try { + setLoadingDel(true) + const response = await apiDeletePortofolio(param.id) + if (response.success) { + ComponentGlobal_NotifikasiBerhasil(response.message) + router.back() + } else { + ComponentGlobal_NotifikasiGagal(response.message); + } + } catch (error) { + console.error(error) + ComponentGlobal_NotifikasiGagal("Gagal menghapus portofolio"); + } finally { + setLoadingDel(false) + } + } + + async function funGetPortofolio() { + try { + const response = await apiGetOnePortofolioById(param.id, "bisnis") + const response2 = await funGetUserIdByToken() + if (response.success) { + setDataPorto(response.data) + setUserLoginId(response2) + } + } catch (error) { + console.error(error); + } + } + + useShallowEffect(() => { + funGetPortofolio() + }, []); + + + return ( + <> + {userLoginId === dataPorto?.authorId ? ( + + ) : ( + "" + )} + + setModal(false)} + buttonKiri={ + + } + buttonKanan={ + + } + /> + + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/component/button_more_new.tsx b/src/app_modules/katalog/portofolio/component/button_more_new.tsx new file mode 100644 index 00000000..d28ed708 --- /dev/null +++ b/src/app_modules/katalog/portofolio/component/button_more_new.tsx @@ -0,0 +1,87 @@ +import { RouterPortofolio } from "@/app/lib/router_hipmi/router_katalog"; +import { RouterMap } from "@/app/lib/router_hipmi/router_map"; +import { UIGlobal_Drawer } from "@/app_modules/_global/ui"; +import { ActionIcon } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconEdit, IconPhotoEdit, IconId, IconMapPin2, IconMapPin, IconDotsVertical } from "@tabler/icons-react"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import { apiGetOnePortofolioById } from "../lib/api_portofolio"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; + +export default function ComponentPortofolio_ButtonMoreNew() { + const param = useParams<{ id: string }>() + const [userLoginId, setUserLoginId] = useState("") + const [authorId, setAuthorId] = useState("") + const [openDrawer, setOpenDrawer] = useState(false) + + const listPage = [ + { + id: "1", + name: "Edit detail ", + icon: , + path: RouterPortofolio.edit_data_bisnis + `${param.id}`, + }, + { + id: "2", + name: "Edit logo ", + icon: , + path: RouterPortofolio.edit_logo_bisnis + `${param.id}`, + }, + { + id: "3", + name: "Edit sosial media", + icon: , + path: RouterPortofolio.edit_medsos_bisnis + `${param.id}`, + }, + { + id: "4", + name: "Edit data map", + icon: , + path: RouterMap.edit + `${param.id}`, + }, + { + id: "5", + name: "Custom pin map", + icon: , + path: RouterMap.custom_pin + `${param.id}`, + }, + ]; + + + async function funGetPortofolio() { + try { + const response = await apiGetOnePortofolioById(param.id, "bisnis") + const response2 = await funGetUserIdByToken() + if (response.success) { + setAuthorId(response.data.authorId) + setUserLoginId(response2) + } + } catch (error) { + console.error(error); + } + } + + useShallowEffect(() => { + funGetPortofolio() + }, []); + + + return ( + <> + {userLoginId === authorId ? ( + setOpenDrawer(true)}> + + + ) : ( + + )} + + setOpenDrawer(false)} + component={listPage} + /> + + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/index.ts b/src/app_modules/katalog/portofolio/index.ts index 9d31be0f..e123bbff 100644 --- a/src/app_modules/katalog/portofolio/index.ts +++ b/src/app_modules/katalog/portofolio/index.ts @@ -11,6 +11,8 @@ import LayoutPortofolio_EditDataBisnis from "./edit/data/layout"; import LayoutPortofolio_EditLogoBisnis from "./edit/logo/layout"; import LayoutPortofolio_EditMedsosBisnis from "./edit/medsos/layout"; import ListDetailPortofolioNew from './view/list_detail_portofolio_new'; +import Portofolio_UiDetailNew from './ui/ui_detail_portofolio_new'; +import PortofolioLayoutNew from './ui/ui_layout_new'; export { CreatePortofolio, @@ -28,4 +30,6 @@ export { export type { IListPortofolio }; export { Portofolio_ViewListDetail } from "./view/view_list_detail_portofolio"; export { ListDetailPortofolioNew } +export { Portofolio_UiDetailNew } +export { PortofolioLayoutNew } diff --git a/src/app_modules/katalog/portofolio/lib/api_portofolio.ts b/src/app_modules/katalog/portofolio/lib/api_portofolio.ts index a4c460db..c152514d 100644 --- a/src/app_modules/katalog/portofolio/lib/api_portofolio.ts +++ b/src/app_modules/katalog/portofolio/lib/api_portofolio.ts @@ -1,4 +1,19 @@ export const apiGetPortofolioByProfile = async (path?: string) => { const response = await fetch(`/api/new/portofolio${(path) ? path : ''}`) return await response.json().catch(() => null) +} + +export const apiGetOnePortofolioById = async (path: string, cat:string) => { + const response = await fetch(`/api/new/portofolio/${path}?cat=${cat}`); + return await response.json().catch(() => null); +} + +export const apiDeletePortofolio = async (path: string) => { + const response = await fetch(`/api/new/portofolio/${path}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }); + return await response.json().catch(() => null); } \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/lib/type_portofolio.ts b/src/app_modules/katalog/portofolio/lib/type_portofolio.ts index 637935e1..1a543763 100644 --- a/src/app_modules/katalog/portofolio/lib/type_portofolio.ts +++ b/src/app_modules/katalog/portofolio/lib/type_portofolio.ts @@ -3,4 +3,33 @@ export interface IListPortofolio { id_Portofolio: string profileId: string namaBisnis: string +} + +export interface IDetailPortofolioBisnis { + id_Portofolio: string + namaBisnis: string + alamatKantor: string + tlpn: string + deskripsi: string + logoId: string + bidangBisnis: string + authorId: string +} + +export interface IDetailPortofolioLokasi { + mapId: string + logoId: string + namePin: string + latitude: string + longitude: string + imageId: string + pinId: string +} + +export interface IDetailPortofolioSosmed { + facebook: string + twitter: string + instagram: string + tiktok: string + youtube: string } \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/ui/ui_detail_data_new.tsx b/src/app_modules/katalog/portofolio/ui/ui_detail_data_new.tsx new file mode 100644 index 00000000..92d2cead --- /dev/null +++ b/src/app_modules/katalog/portofolio/ui/ui_detail_data_new.tsx @@ -0,0 +1,131 @@ +import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import { ComponentGlobal_LoadImage } from "@/app_modules/_global/component"; +import { Paper, Stack, Group, Title, SimpleGrid, Box, Grid, Divider, Text } from "@mantine/core"; +import { IconBuildingSkyscraper, IconListDetails, IconPhoneCall, IconMapPin, IconPinned } from "@tabler/icons-react"; +import { useState } from "react"; +import { IDetailPortofolioBisnis } from "../lib/type_portofolio"; +import { useParams } from "next/navigation"; +import { apiGetOnePortofolioById } from "../lib/api_portofolio"; +import { useShallowEffect } from "@mantine/hooks"; +import SkeletonDetailBisnis from "./ui_skeleton_detail_bisnis"; + +export default function Portofolio_UiDetailDataNew() { + const [loading, setLoading] = useState(true) + const param = useParams<{ id: string }>() + const [dataPorto, setDataPorto] = useState(); + + async function funGetPortofolio() { + try { + setLoading(true) + const response = await apiGetOnePortofolioById(param.id, "bisnis"); + if (response.success) { + setDataPorto(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + useShallowEffect(() => { + funGetPortofolio() + }, []); + + return ( + <> + + { + loading ? + + : + + + Data Bisnis + + id: {" "} + + #{dataPorto?.id_Portofolio} + + + + + + + + + + + + + + + + + + {dataPorto?.namaBisnis} + + + + + + + + {dataPorto?.bidangBisnis} + + + + + + + + {dataPorto?.tlpn} + + + + + + + + {dataPorto?.alamatKantor} + + + + + + + + + + + + + Tentang Kami + + + {dataPorto?.deskripsi} + + + } + + + + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/ui/ui_detail_map_new.tsx b/src/app_modules/katalog/portofolio/ui/ui_detail_map_new.tsx new file mode 100644 index 00000000..54faf735 --- /dev/null +++ b/src/app_modules/katalog/portofolio/ui/ui_detail_map_new.tsx @@ -0,0 +1,129 @@ +import { APIs } from "@/app/lib"; +import { AccentColor } from "@/app_modules/_global/color"; +import { defaultMapZoom } from "@/app_modules/map/lib/default_lat_long"; +import { Paper, Stack, Title, Avatar, Skeleton } from "@mantine/core"; +import "mapbox-gl/dist/mapbox-gl.css"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import { AttributionControl, Map, Marker, NavigationControl, ScaleControl, } from "react-map-gl"; +import { IDetailPortofolioLokasi } from "../lib/type_portofolio"; +import { apiGetOnePortofolioById } from "../lib/api_portofolio"; +import { useShallowEffect } from "@mantine/hooks"; +import { ComponentMap_DetailData, ComponentMap_DrawerDetailData } from "@/app_modules/map/_component"; + +export default function Portofolio_UiMapNew({ mapboxToken }: { mapboxToken: string }) { + const [loading, setLoading] = useState(true) + const param = useParams<{ id: string }>() + const [dataPorto, setDataPorto] = useState() + const [openDrawer, setOpenDrawer] = useState(false) + + async function funGetPortofolio() { + try { + setLoading(true) + const response = await apiGetOnePortofolioById(param.id, "lokasi"); + if (response.success) { + setDataPorto(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + useShallowEffect(() => { + funGetPortofolio() + }, []); + + + return ( + <> + + + + Lokasi Bisnis + + { + loading ? + + : + + { + setOpenDrawer(true); + }} + pitchAlignment="auto" + > + + + + + + + + + + + } + + + + setOpenDrawer(false)} + mapId={String(dataPorto?.mapId)} + component={} + /> + + + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/ui/ui_detail_media_new.tsx b/src/app_modules/katalog/portofolio/ui/ui_detail_media_new.tsx new file mode 100644 index 00000000..132fe798 --- /dev/null +++ b/src/app_modules/katalog/portofolio/ui/ui_detail_media_new.tsx @@ -0,0 +1,131 @@ +import { AccentColor } from "@/app_modules/_global/color"; +import { Paper, Title, Stack, Grid, Text, Skeleton, Box } from "@mantine/core"; +import { IconBrandFacebook, IconBrandInstagram, IconBrandTiktok, IconBrandTwitter, IconBrandYoutube } from "@tabler/icons-react"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import { IDetailPortofolioSosmed } from "../lib/type_portofolio"; +import { apiGetOnePortofolioById } from "../lib/api_portofolio"; +import { useShallowEffect } from "@mantine/hooks"; + +export default function Portofolio_UiSosialMediaNew() { + const [loading, setLoading] = useState(true) + const param = useParams<{ id: string }>() + const [dataPorto, setDataPorto] = useState(); + + async function funGetPortofolio() { + try { + setLoading(true) + const response = await apiGetOnePortofolioById(param.id, "sosmed"); + if (response.success) { + setDataPorto(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + useShallowEffect(() => { + funGetPortofolio() + }, []); + + + return ( + <> + + Media Sosial Bisnis + { + loading ? + + {[...Array(4)].map((_, index) => ( + + + + + + + + + + + ))} + + : + + + + + + + {dataPorto?.facebook ? ( + {dataPorto?.facebook} + ) : ( + "-" + )} + + + + + + + + {dataPorto?.instagram ? ( + {dataPorto?.instagram} + ) : ( + "-" + )} + + + + + + + + {dataPorto?.tiktok ? ( + {dataPorto?.tiktok} + ) : ( + "-" + )} + + + + + + + + {dataPorto?.twitter ? ( + {dataPorto?.twitter} + ) : ( + "-" + )} + + + + + + + + {dataPorto?.youtube ? ( + {dataPorto?.youtube} + ) : ( + "-" + )} + + + + + } + + + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/ui/ui_detail_portofolio_new.tsx b/src/app_modules/katalog/portofolio/ui/ui_detail_portofolio_new.tsx new file mode 100644 index 00000000..a0203528 --- /dev/null +++ b/src/app_modules/katalog/portofolio/ui/ui_detail_portofolio_new.tsx @@ -0,0 +1,19 @@ +'use client' +import { Stack } from "@mantine/core"; +import Portofolio_UiDetailDataNew from "./ui_detail_data_new"; +import Portofolio_UiMapNew from "./ui_detail_map_new"; +import Portofolio_UiSosialMediaNew from "./ui_detail_media_new"; +import ComponentPortofolio_ButtonDeleteNew from "../component/button_delete_new"; + +export default function Portofolio_UiDetailNew({ mapboxToken }: { mapboxToken: string }) { + return ( + <> + + + + + + + + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/ui/ui_layout_new.tsx b/src/app_modules/katalog/portofolio/ui/ui_layout_new.tsx new file mode 100644 index 00000000..9f6b5d75 --- /dev/null +++ b/src/app_modules/katalog/portofolio/ui/ui_layout_new.tsx @@ -0,0 +1,20 @@ +'use client' +import { UIGlobal_LayoutHeaderTamplate, UIGlobal_LayoutTamplate } from "@/app_modules/_global/ui"; +import ComponentPortofolio_ButtonMoreNew from "../component/button_more_new"; + +export default function PortofolioLayoutNew({ children }: { children: any }) { + return ( + <> + } + /> + } + > + {children} + + + ) +} \ No newline at end of file diff --git a/src/app_modules/katalog/portofolio/ui/ui_skeleton_detail_bisnis.tsx b/src/app_modules/katalog/portofolio/ui/ui_skeleton_detail_bisnis.tsx new file mode 100644 index 00000000..b8708f23 --- /dev/null +++ b/src/app_modules/katalog/portofolio/ui/ui_skeleton_detail_bisnis.tsx @@ -0,0 +1,35 @@ +import { Box, Grid, Group, Skeleton, Stack } from "@mantine/core"; + +export default function SkeletonDetailBisnis() { + return <> + + + + + + + + {[...Array(4)].map((_, index) => ( + + + + + + + + + + + ))} + + + + + + + + + + + ; +} \ No newline at end of file From 231f713005cb1b16b3c671cb52abedbe58607c0c Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Sun, 8 Dec 2024 17:16:05 +0800 Subject: [PATCH 028/595] Fix event & auth Deskripsi: - Fix event kondisi user melewatkan event - Fix bug force-dynamic di beberapa api --- src/app/api/auth/check/route.ts | 14 +++--- src/app/api/auth/login/route.ts | 35 ++++++-------- src/app/api/auth/logout/route.ts | 39 +++++++-------- src/app/api/auth/register/route.ts | 28 ++++------- src/app/api/auth/resend/route.ts | 30 +++++++----- src/app/api/auth/validasi/route.ts | 47 ++++-------------- src/app/api/check-cookies/route.ts | 2 +- src/app/api/event/list-peserta/route.ts | 1 + src/app/api/origin-url/route.ts | 3 +- src/app/api/test-scroll/route.ts | 5 +- src/app/api/validasi/route.ts | 8 ++-- src/app/api/zz-makuro/route.ts | 3 +- src/app/auth/api/login/route.ts | 8 ++-- src/app/auth/api/logout/route.ts | 5 +- src/app/dev/event/konfirmasi/[id]/page.tsx | 5 -- src/app_modules/event/_ui/konfirmasi.tsx | 55 +++++++++------------- 16 files changed, 121 insertions(+), 167 deletions(-) diff --git a/src/app/api/auth/check/route.ts b/src/app/api/auth/check/route.ts index ff617b41..d8176b13 100644 --- a/src/app/api/auth/check/route.ts +++ b/src/app/api/auth/check/route.ts @@ -1,10 +1,12 @@ import { prisma } from "@/app/lib"; import { data } from "autoprefixer"; -import { NextResponse } from "next/server"; +import { NextRequest, NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; -export async function GET(request: Request) { - const { searchParams } = new URL(request.url); - const id = searchParams.get("id"); +export async function GET(request: NextRequest) { + const id = request.nextUrl.searchParams.get("id"); + // const { searchParams } = new URL(request.url); + // const id = searchParams.get("id"); try { const data = await prisma.kodeOtp.findFirst({ @@ -12,10 +14,10 @@ export async function GET(request: Request) { id: id as string, }, }); - return new Response(JSON.stringify({ data }), { status: 200 }); + return NextResponse.json(data, { status: 200 }); } catch (error) { console.log(error); } - return new Response(JSON.stringify({ data: null }), { status: 404 }); + return NextResponse.json(null, { status: 500 }); } diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index 3aac1607..97c36643 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -18,14 +18,11 @@ export async function POST(req: Request) { const sendWa = await res.json(); if (sendWa.status !== "success") - return new Response( - JSON.stringify({ - success: false, - message: "Nomor Whatsapp Tidak Aktif", - }), + return NextResponse.json( + { success: false, message: "Nomor Whatsapp Tidak Aktif" }, { status: 400 } ); - + const createOtpId = await prisma.kodeOtp.create({ data: { nomor: nomor, @@ -34,32 +31,30 @@ export async function POST(req: Request) { }); if (!createOtpId) - return new Response( - JSON.stringify({ - success: false, - message: "Gagal Membuat Kode OTP", - }), + return NextResponse.json( + { success: false, message: "Gagal Membuat Kode OTP" }, { status: 400 } ); - return new Response( - JSON.stringify({ + return NextResponse.json( + { success: true, message: "Kode Verifikasi Dikirim", kodeId: createOtpId.id, - }), + }, { status: 200 } ); } catch (error) { console.log(error); - return new Response( - JSON.stringify({ - success: false, - message: "Server Whatsapp Error !!", - }), + + return NextResponse.json( + { success: false, message: "Server Whatsapp Error !! " }, { status: 500 } ); } } - return NextResponse.json({ success: false }); + return NextResponse.json( + { success: false, message: "Method Not Allowed" }, + { status: 405 } + ); } diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts index 9a5356ff..0f9be556 100644 --- a/src/app/api/auth/logout/route.ts +++ b/src/app/api/auth/logout/route.ts @@ -1,28 +1,21 @@ import { prisma } from "@/app/lib"; import { cookies } from "next/headers"; -export async function GET(request: Request) { - const { searchParams } = new URL(request.url); - const id = searchParams.get("id"); +import { NextRequest, NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; +export async function GET(request: NextRequest) { + const id = request.nextUrl.searchParams.get("id"); + // const { searchParams } = new URL(request.url); + // const id = searchParams.get("id"); - const delToken = await prisma.userSession.delete({ - where: { - userId: id as string, - }, - }); + const delToken = await prisma.userSession.delete({ + where: { + userId: id as string, + }, + }); const del = cookies().delete(process.env.NEXT_PUBLIC_BASE_SESSION_KEY!); - return new Response(JSON.stringify({ success: true, message: "Logout Berhasil" }), {status: 200}); -} - -// import { cookies } from "next/headers"; -// import { NextResponse } from "next/server"; - -// export async function GET() { -// cookies().set({ -// name: "mySession", -// value: "", -// maxAge: 0, -// }); - -// return NextResponse.json({ status: 200, message: "Logout" }); -// } + return NextResponse.json( + { success: true, message: "Logout Berhasil" }, + { status: 200 } + ); + } diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts index 19366387..f25500d7 100644 --- a/src/app/api/auth/register/route.ts +++ b/src/app/api/auth/register/route.ts @@ -1,5 +1,6 @@ import { sessionCreate } from "@/app/auth/_lib/session_create"; import prisma from "@/app/lib/prisma"; +import { NextResponse } from "next/server"; export async function POST(req: Request) { if (req.method === "POST") { @@ -12,11 +13,8 @@ export async function POST(req: Request) { }); if (cekUsername) - return new Response( - JSON.stringify({ - success: false, - message: "Username sudah digunakan", - }), + return NextResponse.json( + { success: false, message: "Username sudah digunakan" }, { status: 400 } ); @@ -43,28 +41,22 @@ export async function POST(req: Request) { }); if (!createUserSession) - return new Response( - JSON.stringify({ - success: false, - message: "Gagal Membuat Session", - }), + return NextResponse.json( + { success: false, message: "Gagal Membuat Session" }, { status: 400 } ); } catch (error) { console.log(error); } - return new Response( - JSON.stringify({ - success: true, - message: "Berhasil Login", - }), - + return NextResponse.json( + { success: true, message: "Berhasil Login", data: createUser }, { status: 200 } ); } - return new Response( - JSON.stringify({ success: false, message: "Method Not Allowed" }), + + return NextResponse.json( + { success: false, message: "Method Not Allowed" }, { status: 405 } ); } diff --git a/src/app/api/auth/resend/route.ts b/src/app/api/auth/resend/route.ts index 3aac1607..3e02b0ef 100644 --- a/src/app/api/auth/resend/route.ts +++ b/src/app/api/auth/resend/route.ts @@ -18,11 +18,11 @@ export async function POST(req: Request) { const sendWa = await res.json(); if (sendWa.status !== "success") - return new Response( - JSON.stringify({ + return NextResponse.json( + { success: false, message: "Nomor Whatsapp Tidak Aktif", - }), + }, { status: 400 } ); @@ -34,32 +34,36 @@ export async function POST(req: Request) { }); if (!createOtpId) - return new Response( - JSON.stringify({ + return NextResponse.json( + { success: false, message: "Gagal Membuat Kode OTP", - }), + }, { status: 400 } ); - return new Response( - JSON.stringify({ + return NextResponse.json( + { success: true, message: "Kode Verifikasi Dikirim", kodeId: createOtpId.id, - }), + }, { status: 200 } ); } catch (error) { console.log(error); - return new Response( - JSON.stringify({ + + return NextResponse.json( + { success: false, message: "Server Whatsapp Error !!", - }), + }, { status: 500 } ); } } - return NextResponse.json({ success: false }); + return NextResponse.json( + { success: false, message: "Method Not Allowed" }, + { status: 405 } + ); } diff --git a/src/app/api/auth/validasi/route.ts b/src/app/api/auth/validasi/route.ts index f865cc07..2f432b70 100644 --- a/src/app/api/auth/validasi/route.ts +++ b/src/app/api/auth/validasi/route.ts @@ -1,9 +1,5 @@ import { sessionCreate } from "@/app/auth/_lib/session_create"; import prisma from "@/app/lib/prisma"; -import { ServerEnv } from "@/app/lib/server_env"; -import { sealData } from "iron-session"; -import { revalidatePath } from "next/cache"; -import { cookies } from "next/headers"; import { NextResponse } from "next/server"; export async function POST(req: Request) { @@ -23,10 +19,9 @@ export async function POST(req: Request) { }, }); - if (dataUser === null) - return new Response( - JSON.stringify({ success: false, message: "Nomor Belum Terdaftar" }), + return NextResponse.json( + { success: false, message: "Nomor Belum Terdaftar" }, { status: 404 } ); @@ -59,49 +54,27 @@ export async function POST(req: Request) { }); if (!createUserSession) - return new Response( - JSON.stringify({ success: false, message: "Gagal Membuat Session" }), + return NextResponse.json( + { success: false, message: "Gagal Membuat Session" }, { status: 400 } ); } catch (error) { console.log(error); } - // if (data) { - // const res = await sealData( - // JSON.stringify({ - // id: data.id, - // username: data.username, - // }), - // { - // password: ServerEnv.value?.WIBU_PWD as string, - // } - // ); - - // cookies().set({ - // name: "mySession", - // value: res, - // maxAge: 60 * 60 * 24 * 7, - // }); - - // revalidatePath("/dev/home"); - - // return NextResponse.json({ status: 200, data }); - // } - - // return NextResponse.json({ success: true }); - return new Response( - JSON.stringify({ + return NextResponse.json( + { success: true, message: "Berhasil Login", roleId: dataUser.masterUserRoleId, active: dataUser.active, - }), + }, { status: 200 } ); } - return new Response( - JSON.stringify({ success: false, message: "Method Not Allowed" }), + + return NextResponse.json( + { success: false, message: "Method Not Allowed" }, { status: 405 } ); } diff --git a/src/app/api/check-cookies/route.ts b/src/app/api/check-cookies/route.ts index b02fce67..ebc446be 100644 --- a/src/app/api/check-cookies/route.ts +++ b/src/app/api/check-cookies/route.ts @@ -8,7 +8,7 @@ export async function GET(req: Request) { if (!c || !c?.value || _.isEmpty(c?.value) || _.isUndefined(c?.value)) { return NextResponse.json({ success: false }); - // return new Response(JSON.stringify({ success: false })); + } return NextResponse.json({ success: true }); } diff --git a/src/app/api/event/list-peserta/route.ts b/src/app/api/event/list-peserta/route.ts index be0037a6..6acbbc08 100644 --- a/src/app/api/event/list-peserta/route.ts +++ b/src/app/api/event/list-peserta/route.ts @@ -1,4 +1,5 @@ import { event_newGetListPesertaById } from "@/app_modules/event/fun"; +import { Event_getListPesertaById } from "@/app_modules/event/fun/get/get_list_peserta_by_id"; import { toNumber } from "lodash"; import { NextResponse } from "next/server"; diff --git a/src/app/api/origin-url/route.ts b/src/app/api/origin-url/route.ts index 186624ae..4cbd28d7 100644 --- a/src/app/api/origin-url/route.ts +++ b/src/app/api/origin-url/route.ts @@ -1,6 +1,7 @@ import { headers } from "next/headers"; +import { NextResponse } from "next/server"; export async function GET(req: Request) { const origin = new URL(req.url).origin; - return new Response(JSON.stringify({ success: true, origin })); + return NextResponse.json({ success: true, origin }); } diff --git a/src/app/api/test-scroll/route.ts b/src/app/api/test-scroll/route.ts index c722ba33..f2fdc9ea 100644 --- a/src/app/api/test-scroll/route.ts +++ b/src/app/api/test-scroll/route.ts @@ -1,8 +1,11 @@ import prisma from "@/app/lib/prisma"; +import { NextResponse } from "next/server"; export async function GET(req: Request) { const page = new URL(req.url).searchParams.get("page"); - if (!page) return new Response("page require", { status: 400 }); + if (!page) + return NextResponse.json({ message: "Page not found" }, { status: 400 }); + const res = await prisma.projectCollaboration_Message.findMany({ take: 5, skip: +page * 5 - 5, diff --git a/src/app/api/validasi/route.ts b/src/app/api/validasi/route.ts index 65467cdb..4562aa95 100644 --- a/src/app/api/validasi/route.ts +++ b/src/app/api/validasi/route.ts @@ -1,7 +1,9 @@ +import { NextResponse } from "next/server"; + export async function GET(req: Request) { const auth = req.headers.get("Authorization"); const token = auth?.split(" ")[1]; - if (!token) - return new Response(JSON.stringify({ success: false }), { status: 401 }); - return new Response(JSON.stringify({ success: true })); + if (!token) return NextResponse.json({ success: false }, { status: 401 }); + return NextResponse.json({ success: true }); + } diff --git a/src/app/api/zz-makuro/route.ts b/src/app/api/zz-makuro/route.ts index df7b0d72..da2e4655 100644 --- a/src/app/api/zz-makuro/route.ts +++ b/src/app/api/zz-makuro/route.ts @@ -1,7 +1,8 @@ import { headers } from "next/headers"; +import { NextResponse } from "next/server"; export async function GET( req: Request) { const origin = new URL(req.url).origin; - return new Response(JSON.stringify({ success: true, origin })); + return NextResponse.json({ success: true, origin }); } diff --git a/src/app/auth/api/login/route.ts b/src/app/auth/api/login/route.ts index 370b5bad..eb59a9f0 100644 --- a/src/app/auth/api/login/route.ts +++ b/src/app/auth/api/login/route.ts @@ -1,5 +1,6 @@ import { prisma } from "@/app/lib"; import { sessionCreate } from "../../_lib/session_create"; +import { NextResponse } from "next/server"; export async function POST(req: Request) { const user = await prisma.user.findUnique({ @@ -12,10 +13,7 @@ export async function POST(req: Request) { }, }); - if (!user) - return new Response( - JSON.stringify({ success: false, message: "User not found" }), {status: 404} - ); + if (!user) return NextResponse.json({ success: false }, { status: 404 }); const token = await sessionCreate({ sessionKey: process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, @@ -23,5 +21,5 @@ export async function POST(req: Request) { user: user as any, }); - return new Response(JSON.stringify({ success: true, token })); + return NextResponse.json({ success: true, token }); } diff --git a/src/app/auth/api/logout/route.ts b/src/app/auth/api/logout/route.ts index af733975..25aab3db 100644 --- a/src/app/auth/api/logout/route.ts +++ b/src/app/auth/api/logout/route.ts @@ -1,5 +1,8 @@ import { cookies } from "next/headers"; +import { NextResponse } from "next/server"; + export async function GET() { const del = cookies().delete(process.env.NEXT_PUBLIC_BASE_SESSION_KEY!); - return new Response(JSON.stringify({ success: true })); + + return NextResponse.json({ success: true, message: "Logout Berhasil" }); } diff --git a/src/app/dev/event/konfirmasi/[id]/page.tsx b/src/app/dev/event/konfirmasi/[id]/page.tsx index 3a0af55b..bb2200b5 100644 --- a/src/app/dev/event/konfirmasi/[id]/page.tsx +++ b/src/app/dev/event/konfirmasi/[id]/page.tsx @@ -1,10 +1,5 @@ import { newFunGetUserId } from "@/app/lib/new_fun_user_id"; -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; import Ui_Konfirmasi from "@/app_modules/event/_ui/konfirmasi"; -import { event_funCheckPesertaByUserId } from "@/app_modules/event/fun"; -import { event_getOneById } from "@/app_modules/event/fun/get/get_one_by_id"; -import moment from "moment"; -import { redirect } from "next/navigation"; export default async function Page({ params, diff --git a/src/app_modules/event/_ui/konfirmasi.tsx b/src/app_modules/event/_ui/konfirmasi.tsx index 8b1814b7..fb72b2ed 100644 --- a/src/app_modules/event/_ui/konfirmasi.tsx +++ b/src/app_modules/event/_ui/konfirmasi.tsx @@ -1,38 +1,33 @@ "use client"; -import { - UIGlobal_LayoutDefault, - UIGlobal_LayoutTamplate, -} from "@/app_modules/_global/ui"; -import { - Button, - Card, - Center, - Group, - Paper, - Skeleton, - Stack, - Text, - Title, -} from "@mantine/core"; -import { MODEL_EVENT } from "../model/interface"; -import { useShallowEffect } from "@mantine/hooks"; -import { AccentColor, MainColor } from "@/app_modules/_global/color"; -import { event_funCheckKehadiran, event_funUpdateKehadiran } from "../fun"; +import { API_RouteEvent } from "@/app/lib/api_user_router/route_api_event"; +import { RouterEvent } from "@/app/lib/router_hipmi/router_event"; +import { MainColor } from "@/app_modules/_global/color"; +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; import { ComponentGlobal_NotifikasiBerhasil, ComponentGlobal_NotifikasiGagal, } from "@/app_modules/_global/notif_global"; -import { redirect, useRouter } from "next/navigation"; -import { RouterEvent } from "@/app/lib/router_hipmi/router_event"; -import { useState } from "react"; -import { API_RouteEvent } from "@/app/lib/api_user_router/route_api_event"; -import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; -import moment from "moment"; -import { gs_event_hotMenu } from "../global_state"; +import { + UIGlobal_LayoutDefault +} from "@/app_modules/_global/ui"; +import { + Button, + Center, + Group, + Skeleton, + Stack, + Text +} from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; import { useAtom } from "jotai"; -import { Event_funJoinEvent } from "../fun/create/fun_join_event"; +import moment from "moment"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { event_funUpdateKehadiran } from "../fun"; import { Event_funJoinAndConfirmEvent } from "../fun/create/fun_join_and_confirm"; +import { gs_event_hotMenu } from "../global_state"; +import { MODEL_EVENT } from "../model/interface"; export default function Ui_Konfirmasi({ userLoginId, @@ -70,7 +65,6 @@ export default function Ui_Konfirmasi({ API_RouteEvent.check_peserta({ eventId: eventId, userId: userLoginId }) ); const data = await res.json(); - console.log("cek peserta", data); setIsJoin(data); } @@ -87,9 +81,6 @@ export default function Ui_Konfirmasi({ setIsPresent(data); } - // console.log("kehadiran:", isPresent); - // console.log("data:", data); - if (data == null && isPresent == null) { return ; } @@ -102,7 +93,7 @@ export default function Ui_Konfirmasi({ ); } - if (moment(data?.tanggal).diff(moment(), "minute") < 0) { + if (moment(data?.tanggalSelesai).diff(moment(), "minute") < 0) { return ( <> From 1dfaa4bb74bf4a3cf01f650ead540493dbf18be7 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 9 Dec 2024 09:10:16 +0800 Subject: [PATCH 029/595] Fix auth --- src/app/api/auth/check/route.ts | 1 + src/app/api/auth/logout/route.ts | 13 ++- src/app/api/auth/validasi/route.ts | 117 +++++++++++++------------ src/app_modules/auth/validasi/view.tsx | 4 +- 4 files changed, 69 insertions(+), 66 deletions(-) diff --git a/src/app/api/auth/check/route.ts b/src/app/api/auth/check/route.ts index d8176b13..b51a6f31 100644 --- a/src/app/api/auth/check/route.ts +++ b/src/app/api/auth/check/route.ts @@ -14,6 +14,7 @@ export async function GET(request: NextRequest) { id: id as string, }, }); + return NextResponse.json(data, { status: 200 }); } catch (error) { console.log(error); diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts index 0f9be556..44a4a2ab 100644 --- a/src/app/api/auth/logout/route.ts +++ b/src/app/api/auth/logout/route.ts @@ -1,4 +1,3 @@ -import { prisma } from "@/app/lib"; import { cookies } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; export const dynamic = "force-dynamic"; @@ -7,15 +6,15 @@ export async function GET(request: NextRequest) { // const { searchParams } = new URL(request.url); // const id = searchParams.get("id"); - const delToken = await prisma.userSession.delete({ - where: { - userId: id as string, - }, - }); + // const delToken = await prisma.userSession.delete({ + // where: { + // userId: id as string, + // }, + // }); const del = cookies().delete(process.env.NEXT_PUBLIC_BASE_SESSION_KEY!); return NextResponse.json( { success: true, message: "Logout Berhasil" }, { status: 200 } ); - } +} diff --git a/src/app/api/auth/validasi/route.ts b/src/app/api/auth/validasi/route.ts index 2f432b70..05ee6b26 100644 --- a/src/app/api/auth/validasi/route.ts +++ b/src/app/api/auth/validasi/route.ts @@ -6,73 +6,76 @@ export async function POST(req: Request) { if (req.method === "POST") { const { nomor } = await req.json(); - const dataUser = await prisma.user.findUnique({ - where: { - nomor: nomor, - }, - select: { - id: true, - nomor: true, - username: true, - active: true, - masterUserRoleId: true, - }, - }); - - if (dataUser === null) - return NextResponse.json( - { success: false, message: "Nomor Belum Terdaftar" }, - { status: 404 } - ); - - const token = await sessionCreate({ - sessionKey: process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, - encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, - user: dataUser as any, - }); - - const cekSessionUser = await prisma.userSession.findFirst({ - where: { - userId: dataUser.id, - }, - }); - - if (cekSessionUser !== null) { - await prisma.userSession.delete({ - where: { - userId: dataUser.id, - }, - }); - } - try { - const createUserSession = await prisma.userSession.create({ - data: { - token: token as string, - userId: dataUser.id, + const dataUser = await prisma.user.findUnique({ + where: { + nomor: nomor, + }, + select: { + id: true, + nomor: true, + username: true, + active: true, + masterUserRoleId: true, }, }); - if (!createUserSession) + if (dataUser === null) return NextResponse.json( - { success: false, message: "Gagal Membuat Session" }, - { status: 400 } + { success: false, message: "Nomor Belum Terdaftar" }, + { status: 404 } ); + + const token = await sessionCreate({ + sessionKey: process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, + encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, + user: dataUser as any, + }); + + // const cekSessionUser = await prisma.userSession.findFirst({ + // where: { + // userId: dataUser.id, + // }, + // }); + + // if (cekSessionUser !== null) { + // await prisma.userSession.delete({ + // where: { + // userId: dataUser.id, + // }, + // }); + // } + + // try { + // const createUserSession = await prisma.userSession.create({ + // data: { + // token: token as string, + // userId: dataUser.id, + // }, + // }); + + // if (!createUserSession) + // return NextResponse.json( + // { success: false, message: "Gagal Membuat Session" }, + // { status: 400 } + // ); + // } catch (error) { + // console.log(error); + // } + + return NextResponse.json( + { + success: true, + message: "Berhasil Login", + roleId: dataUser.masterUserRoleId, + active: dataUser.active, + }, + { status: 200 } + ); } catch (error) { console.log(error); } - - return NextResponse.json( - { - success: true, - message: "Berhasil Login", - roleId: dataUser.masterUserRoleId, - active: dataUser.active, - }, - { status: 200 } - ); } - return NextResponse.json( { success: false, message: "Method Not Allowed" }, { status: 405 } diff --git a/src/app_modules/auth/validasi/view.tsx b/src/app_modules/auth/validasi/view.tsx index d817b00e..2429b25a 100644 --- a/src/app_modules/auth/validasi/view.tsx +++ b/src/app_modules/auth/validasi/view.tsx @@ -70,8 +70,8 @@ export default function Validasi() { const result = await res.json(); onSetData({ - nomor: result.data.nomor, - code: result.data.otp, + nomor: result.nomor, + code: result.otp, }); } From 667d90bdad31ad7edc01b8da14f4c23ed106f21f Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 9 Dec 2024 11:45:22 +0800 Subject: [PATCH 030/595] chore(release): 1.2.20 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa2c2693..dbad195b 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.20](https://github.com/bipproduction/hipmi/compare/v1.2.19...v1.2.20) (2024-12-09) + ## [1.2.19](https://github.com/bipproduction/hipmi/compare/v1.2.18...v1.2.19) (2024-12-06) ## [1.2.18](https://github.com/bipproduction/hipmi/compare/v1.2.17...v1.2.18) (2024-12-04) diff --git a/package.json b/package.json index 33896d0e..b47b092a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.19", + "version": "1.2.20", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 8e4615e25603cdd39c158cd2a1e10b4425653af5 Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 9 Dec 2024 12:02:01 +0800 Subject: [PATCH 031/595] upd: map Deskripsi; - error url get di api - api pada bussines map No Issues --- src/app/api/new/home/route.ts | 1 + src/app/api/new/map/[id]/route.ts | 76 +++++++ src/app/api/new/map/route.ts | 40 ++++ src/app/api/new/portofolio/[id]/route.ts | 1 + src/app/api/new/portofolio/route.ts | 1 + src/app/api/new/user/route.ts | 1 + src/app/dev/map/main/page.tsx | 8 +- .../map/_component/detail_data.tsx | 2 +- src/app_modules/map/_component/drawer_new.tsx | 206 ++++++++++++++++++ src/app_modules/map/lib/api_map.ts | 9 + src/app_modules/map/lib/type_map.ts | 24 ++ src/app_modules/map/ui/ui_map_new.tsx | 120 ++++++++++ src/app_modules/map/view/main_view_new.tsx | 13 ++ 13 files changed, 497 insertions(+), 5 deletions(-) create mode 100644 src/app/api/new/map/[id]/route.ts create mode 100644 src/app/api/new/map/route.ts create mode 100644 src/app_modules/map/_component/drawer_new.tsx create mode 100644 src/app_modules/map/lib/api_map.ts create mode 100644 src/app_modules/map/lib/type_map.ts create mode 100644 src/app_modules/map/ui/ui_map_new.tsx create mode 100644 src/app_modules/map/view/main_view_new.tsx diff --git a/src/app/api/new/home/route.ts b/src/app/api/new/home/route.ts index 3e07f313..bdb110d8 100644 --- a/src/app/api/new/home/route.ts +++ b/src/app/api/new/home/route.ts @@ -1,6 +1,7 @@ import { prisma } from "@/app/lib"; import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; // GET DATA HOME diff --git a/src/app/api/new/map/[id]/route.ts b/src/app/api/new/map/[id]/route.ts new file mode 100644 index 00000000..8047965d --- /dev/null +++ b/src/app/api/new/map/[id]/route.ts @@ -0,0 +1,76 @@ +import { prisma } from "@/app/lib"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + +export async function GET(request: Request, context: { params: { id: string } }) { + try { + const { id } = context.params + const data = await prisma.businessMaps.findUnique({ + where: { + id: id, + }, + select: { + namePin: true, + latitude: true, + longitude: true, + pinId: true, + imageId: true, + Author: { + select: { + Profile: { + select: { + id: true, + name: true, + imageId: true, + } + } + } + }, + Portofolio: { + select: { + id: true, + alamatKantor: true, + tlpn: true, + deskripsi: true, + namaBisnis: true, + MasterBidangBisnis: { + select: { + name: true, + }, + }, + }, + }, + } + }); + + const dataLokasi = { + namePin: data?.namePin, + latitude: data?.latitude, + longitude: data?.longitude, + pinId: data?.pinId, + imageId: data?.imageId, + } + + const dataAuthor = { + id: data?.Author?.Profile?.id, + name: data?.Author?.Profile?.name, + imageId: data?.Author?.Profile?.imageId, + } + + const dataBisnis = { + id: data?.Portofolio?.id, + alamatKantor: data?.Portofolio?.alamatKantor, + tlpn: data?.Portofolio?.tlpn, + deskripsi: data?.Portofolio?.deskripsi, + namaBisnis: data?.Portofolio?.namaBisnis, + bidangBisnis: data?.Portofolio?.MasterBidangBisnis?.name, + } + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", dataLokasi, dataAuthor, dataBisnis }, { 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/map/route.ts b/src/app/api/new/map/route.ts new file mode 100644 index 00000000..7a07e5ec --- /dev/null +++ b/src/app/api/new/map/route.ts @@ -0,0 +1,40 @@ +import { prisma } from "@/app/lib"; +import _ from "lodash"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + +// GET ALL DATA MAP +export async function GET(request: Request) { + try { + const data = await prisma.businessMaps.findMany({ + where: { + isActive: true, + }, + select: { + id: true, + namePin: true, + latitude: true, + longitude: true, + pinId: true, + Portofolio: { + select: { + logoId: true, + } + } + } + }); + + const dataFix = data.map((v: any) => ({ + ..._.omit(v, ["Portofolio"]), + logoId: v.Portofolio.logoId + })) + + 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/portofolio/[id]/route.ts b/src/app/api/new/portofolio/[id]/route.ts index 5d783cb5..747cabd0 100644 --- a/src/app/api/new/portofolio/[id]/route.ts +++ b/src/app/api/new/portofolio/[id]/route.ts @@ -1,6 +1,7 @@ import { prisma } from "@/app/lib"; import { NextResponse } from "next/server"; import fs from "fs"; +export const dynamic = "force-dynamic"; diff --git a/src/app/api/new/portofolio/route.ts b/src/app/api/new/portofolio/route.ts index b25478d5..fbf91221 100644 --- a/src/app/api/new/portofolio/route.ts +++ b/src/app/api/new/portofolio/route.ts @@ -1,5 +1,6 @@ import { prisma } from "@/app/lib"; import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; // GET ALL DATA PORTOFOLIO BY PROFILE ID diff --git a/src/app/api/new/user/route.ts b/src/app/api/new/user/route.ts index 73a4fb52..8f710f53 100644 --- a/src/app/api/new/user/route.ts +++ b/src/app/api/new/user/route.ts @@ -1,5 +1,6 @@ import { prisma } from "@/app/lib"; import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; // GET ONE DATA USER PROFILE BY PROFILE ID diff --git a/src/app/dev/map/main/page.tsx b/src/app/dev/map/main/page.tsx index 8b1b6fd6..f7867fa4 100644 --- a/src/app/dev/map/main/page.tsx +++ b/src/app/dev/map/main/page.tsx @@ -1,13 +1,13 @@ -import { map_funGetAllMap } from "@/app_modules/map/fun/get/fun_get_all_map"; -import { Map_View } from "@/app_modules/map/view"; +import { Map_ViewNew } from "@/app_modules/map/view/main_view_new"; const mapboxToken = process.env.MAPBOX_TOKEN!; export default async function Page() { - const dataMap = await map_funGetAllMap(); + // const dataMap = await map_funGetAllMap(); return ( <> - + {/* */} + ); } diff --git a/src/app_modules/map/_component/detail_data.tsx b/src/app_modules/map/_component/detail_data.tsx index d76b7b5c..91686323 100644 --- a/src/app_modules/map/_component/detail_data.tsx +++ b/src/app_modules/map/_component/detail_data.tsx @@ -74,7 +74,7 @@ export function ComponentMap_DetailData({ - +{data?.Portofolio.tlpn} + {data?.Portofolio.tlpn} diff --git a/src/app_modules/map/_component/drawer_new.tsx b/src/app_modules/map/_component/drawer_new.tsx new file mode 100644 index 00000000..1a571316 --- /dev/null +++ b/src/app_modules/map/_component/drawer_new.tsx @@ -0,0 +1,206 @@ +import { RouterPortofolio } from "@/app/lib/router_hipmi/router_katalog"; +import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import { ComponentGlobal_LoaderAvatar } from "@/app_modules/_global/component"; +import { ActionIcon, Box, Button, Drawer, Grid, Group, Skeleton, Stack, Text, Title } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconBuildingSkyscraper, IconListDetails, IconMapPin, IconPhoneCall, IconX } from "@tabler/icons-react"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { apiGetOneMapById } from "../lib/api_map"; +import { IDataMap, IDataMapDetailAuthor, IDataMapDetailBisnis } from "../lib/type_map"; +import { ComponentMap_LoadImageMap } from "./comp_load_image_map"; +import { ComponentMap_SkeletonDrawerDetailData } from "./skeleton_detail_data"; + + + + + +// SALAHHHHHHHH ---- HARUS ULANGG +export function ComponentMap_DrawerDetailDataNew({ opened, close, mapId, }: { opened: boolean; close: () => void; mapId: string }) { + const router = useRouter(); + const [dataLokasi, setDataLokasi] = useState() + const [dataAuthor, setDataAuthor] = useState() + const [dataBisnis, setDataBisnis] = useState() + const [loading, setLoading] = useState(false) + + useShallowEffect(() => { + onLoadData(); + }, []); + + async function onLoadData() { + try { + setLoading(true) + const response = await apiGetOneMapById(mapId) + if (response.success) { + setDataLokasi(response.dataLokasi) + setDataAuthor(response.dataAuthor) + setDataBisnis(response.dataBisnis) + } + } catch (error) { + console.error(error) + } finally { + setLoading(false) + } + } + + return ( + <> + close()} + position={"bottom"} + size={"auto"} + withCloseButton={false} + styles={{ + content: { + padding: 0, + position: "absolute", + margin: "auto", + backgroundColor: "transparent", + left: 0, + right: 0, + width: 500, + }, + body: { + backgroundColor: AccentColor.darkblue, + borderTop: `2px solid ${AccentColor.blue}`, + borderRight: `1px solid ${AccentColor.blue}`, + borderLeft: `1px solid ${AccentColor.blue}`, + borderRadius: "20px 20px 0px 0px", + color: "white", + paddingBottom: "5%", + }, + }} + > + + + + {dataLokasi?.namePin ? ( + dataLokasi?.namePin + ) : ( + <Skeleton radius={"xl"} w={100} /> + )} + + + + + + + + + + + { + loading ? + : + + + + onCheckProfile()} + > + + + + + + onCheckProfile()} + > + {dataAuthor?.name} + + + + + + + + + + + + + + + + {dataBisnis?.namaBisnis} + + + + + + + + {dataBisnis?.bidangBisnis} + + + + + + + + +{dataBisnis?.tlpn} + + + + + + + + {dataBisnis?.alamatKantor} + + + + + + + + + + + } + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/app_modules/map/lib/api_map.ts b/src/app_modules/map/lib/api_map.ts new file mode 100644 index 00000000..f0368959 --- /dev/null +++ b/src/app_modules/map/lib/api_map.ts @@ -0,0 +1,9 @@ +export const apiGetAllMap = async (path?: string) => { + const response = await fetch(`/api/new/map${(path) ? path : ''}`) + return await response.json().catch(() => null) +} + +export const apiGetOneMapById = async (path: string) => { + const response = await fetch(`/api/new/map/${path}`); + return await response.json().catch(() => null); +} diff --git a/src/app_modules/map/lib/type_map.ts b/src/app_modules/map/lib/type_map.ts new file mode 100644 index 00000000..f4b21aa6 --- /dev/null +++ b/src/app_modules/map/lib/type_map.ts @@ -0,0 +1,24 @@ +export interface IDataMap { + id: string + namePin: string + latitude: string + longitude: string + pinId: string + logoId: string + imageId: string +} + +export interface IDataMapDetailAuthor { + id: string + name: string + imageId: string +} + +export interface IDataMapDetailBisnis { + id: string + alamatKantor: string + tlpn: string + deskripsi: string + namaBisnis: string + bidangBisnis: string +} \ No newline at end of file diff --git a/src/app_modules/map/ui/ui_map_new.tsx b/src/app_modules/map/ui/ui_map_new.tsx new file mode 100644 index 00000000..6797cee0 --- /dev/null +++ b/src/app_modules/map/ui/ui_map_new.tsx @@ -0,0 +1,120 @@ +"use client"; +import { APIs } from "@/app/lib"; +import { AccentColor } from "@/app_modules/_global/color/color_pallet"; +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import { Avatar, Stack } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import "mapbox-gl/dist/mapbox-gl.css"; +import { useState } from "react"; +import Map, { AttributionControl, Marker, NavigationControl, ScaleControl, } from "react-map-gl"; +import { ComponentMap_DetailData, ComponentMap_DrawerDetailData } from "../_component"; +import { apiGetAllMap } from "../lib/api_map"; +import { defaultLatLong, defaultMapZoom } from "../lib/default_lat_long"; +import { IDataMap } from "../lib/type_map"; + +export function UiMap_MapBoxViewNew({ mapboxToken, }: { mapboxToken: string }) { + const [mapId, setMapId] = useState(""); + const [openDrawer, setOpenDrawer] = useState(false); + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + + useShallowEffect(() => { + onLoadData(); + }, []); + + async function onLoadData() { + try { + setLoading(true) + const response = await apiGetAllMap() + if (response.success) { + setData(response.data) + } + } catch (error) { + console.error(error) + } finally { + setLoading(false) + } + } + + if (!mapboxToken) + return ; + + return ( + <> + + { + loading ? + <> : + + {data.map((e, i) => ( + + + { + setMapId(e.id); + setOpenDrawer(true); + }} + > + + + + + ))} + + + + + + } + + + setOpenDrawer(false)} + mapId={mapId} + component={} + /> + + {/* setOpenDrawer(false)} mapId={mapId}/> */} + + ); +} diff --git a/src/app_modules/map/view/main_view_new.tsx b/src/app_modules/map/view/main_view_new.tsx new file mode 100644 index 00000000..5c9cfb0a --- /dev/null +++ b/src/app_modules/map/view/main_view_new.tsx @@ -0,0 +1,13 @@ +import UIGlobal_LayoutTamplate from "@/app_modules/_global/ui/ui_layout_tamplate"; +import { ComponentMap_Header } from "../_component"; +import { UiMap_MapBoxViewNew } from "../ui/ui_map_new"; + +export async function Map_ViewNew({ mapboxToken }: { mapboxToken: string }) { + return ( + <> + }> + + + + ); +} From a308f41eb1d31e08b45ef2baae11a7e317bb2496 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 9 Dec 2024 13:57:40 +0800 Subject: [PATCH 032/595] Fix event Deskripsi: - Fix logika konfirmasi event - Penambahan tampilan kehadiran peserta di admin --- src/app/api/auth/register/route.ts | 30 ++-- .../admin/event/_view/view_detail_peserta.tsx | 14 ++ .../event/fun/get/get_list_peserta_by_id.ts | 1 + src/app_modules/auth/register/view.tsx | 2 +- src/app_modules/auth/validasi/view.tsx | 10 +- src/app_modules/event/_ui/konfirmasi.tsx | 149 ++++++++++++++---- .../event/component/detail/list_peserta.tsx | 20 +-- .../button/comp_create_new_profile.tsx | 66 ++++---- 8 files changed, 196 insertions(+), 96 deletions(-) diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts index f25500d7..45d3cd9e 100644 --- a/src/app/api/auth/register/route.ts +++ b/src/app/api/auth/register/route.ts @@ -32,22 +32,22 @@ export async function POST(req: Request) { user: createUser as any, }); - try { - const createUserSession = await prisma.userSession.create({ - data: { - token: token as string, - userId: createUser.id, - }, - }); + // try { + // const createUserSession = await prisma.userSession.create({ + // data: { + // token: token as string, + // userId: createUser.id, + // }, + // }); - if (!createUserSession) - return NextResponse.json( - { success: false, message: "Gagal Membuat Session" }, - { status: 400 } - ); - } catch (error) { - console.log(error); - } + // if (!createUserSession) + // return NextResponse.json( + // { success: false, message: "Gagal Membuat Session" }, + // { status: 400 } + // ); + // } catch (error) { + // console.log(error); + // } return NextResponse.json( { success: true, message: "Berhasil Login", data: createUser }, diff --git a/src/app_modules/admin/event/_view/view_detail_peserta.tsx b/src/app_modules/admin/event/_view/view_detail_peserta.tsx index 6c09ef87..c1b76811 100644 --- a/src/app_modules/admin/event/_view/view_detail_peserta.tsx +++ b/src/app_modules/admin/event/_view/view_detail_peserta.tsx @@ -2,6 +2,7 @@ import { MODEL_EVENT_PESERTA } from "@/app_modules/event/model/interface"; import { + Badge, Button, Center, Pagination, @@ -26,6 +27,7 @@ export function AdminEvent_ViewDetailPeserta({ const [isNPage, setNPage] = useState(dataPeserta.nPage); const [isActivePage, setActivePage] = useState(1); + async function onPageClick(p: any) { setActivePage(p); const loadData = await adminEvent_getListPesertaById({ @@ -52,6 +54,15 @@ export function AdminEvent_ViewDetailPeserta({
{e?.User?.Profile?.email}
+ +
+ {e.isPresent ? ( + Hadir + ) : ( + Tidak Hadir + )} +
+ )); @@ -81,6 +92,9 @@ export function AdminEvent_ViewDetailPeserta({
Email
+ +
Konfirmasi Kehadiran
+ {tableRow} diff --git a/src/app_modules/admin/event/fun/get/get_list_peserta_by_id.ts b/src/app_modules/admin/event/fun/get/get_list_peserta_by_id.ts index 7809a34a..b5b9764c 100644 --- a/src/app_modules/admin/event/fun/get/get_list_peserta_by_id.ts +++ b/src/app_modules/admin/event/fun/get/get_list_peserta_by_id.ts @@ -22,6 +22,7 @@ export async function adminEvent_getListPesertaById({ eventId: eventId, }, select: { + isPresent: true, User: { include: { Profile: true, diff --git a/src/app_modules/auth/register/view.tsx b/src/app_modules/auth/register/view.tsx index 7c1de36f..f72d7e50 100644 --- a/src/app_modules/auth/register/view.tsx +++ b/src/app_modules/auth/register/view.tsx @@ -41,7 +41,7 @@ export default function Register() { const res = await fetch(`/api/auth/check?id=${kodeId}`); const result = await res.json(); - onSetData(result.data.nomor); + onSetData(result.nomor); } async function onRegistarsi() { diff --git a/src/app_modules/auth/validasi/view.tsx b/src/app_modules/auth/validasi/view.tsx index 2429b25a..d0b39c01 100644 --- a/src/app_modules/auth/validasi/view.tsx +++ b/src/app_modules/auth/validasi/view.tsx @@ -43,7 +43,7 @@ export default function Validasi() { useShallowEffect(() => { const kodeId = localStorage.getItem("hipmi_auth_code_id"); if (kodeId != null) { - onCheckAuthCode({ kodeId: kodeId as string, onSetData: setData }); + onCheckAuthCode({ kodeId: kodeId as string }); } else { console.log("code id not found"); } @@ -51,25 +51,23 @@ export default function Validasi() { if (triggerOtp) { const kodeId = localStorage.getItem("hipmi_auth_code_id"); if (kodeId != null) { - onCheckAuthCode({ kodeId: kodeId as string, onSetData: setData }); + onCheckAuthCode({ kodeId: kodeId as string }); } else { console.log("code id not found"); } setTriggerOtp(false); } - }, [triggerOtp, setData, setTriggerOtp]); + }, [triggerOtp]); async function onCheckAuthCode({ kodeId, - onSetData, }: { kodeId: string; - onSetData: any; }) { const res = await fetch(`/api/auth/check?id=${kodeId}`); const result = await res.json(); - onSetData({ + setData({ nomor: result.nomor, code: result.otp, }); diff --git a/src/app_modules/event/_ui/konfirmasi.tsx b/src/app_modules/event/_ui/konfirmasi.tsx index fb72b2ed..2062d1b0 100644 --- a/src/app_modules/event/_ui/konfirmasi.tsx +++ b/src/app_modules/event/_ui/konfirmasi.tsx @@ -8,17 +8,8 @@ import { ComponentGlobal_NotifikasiBerhasil, ComponentGlobal_NotifikasiGagal, } from "@/app_modules/_global/notif_global"; -import { - UIGlobal_LayoutDefault -} from "@/app_modules/_global/ui"; -import { - Button, - Center, - Group, - Skeleton, - Stack, - Text -} from "@mantine/core"; +import { UIGlobal_LayoutDefault } from "@/app_modules/_global/ui"; +import { Button, Center, Group, Skeleton, Stack, Text } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { useAtom } from "jotai"; import moment from "moment"; @@ -28,6 +19,7 @@ import { event_funUpdateKehadiran } from "../fun"; import { Event_funJoinAndConfirmEvent } from "../fun/create/fun_join_and_confirm"; import { gs_event_hotMenu } from "../global_state"; import { MODEL_EVENT } from "../model/interface"; +import { Event_funJoinEvent } from "../fun/create/fun_join_event"; export default function Ui_Konfirmasi({ userLoginId, @@ -68,7 +60,7 @@ export default function Ui_Konfirmasi({ setIsJoin(data); } - // CEK KEHADIRAN + // =========== CEK KEHADIRAN ===========// useShallowEffect(() => { onLoadKehadiran(); }, []); @@ -80,11 +72,14 @@ export default function Ui_Konfirmasi({ const data = await res.json(); setIsPresent(data); } + // =========== CEK KEHADIRAN ===========// + // Jika data kosong if (data == null && isPresent == null) { return ; } + // Jika data tidak ada if (data == null) { return ( <> @@ -93,6 +88,7 @@ export default function Ui_Konfirmasi({ ); } + // Jika tanggal acara lewat if (moment(data?.tanggalSelesai).diff(moment(), "minute") < 0) { return ( <> @@ -101,10 +97,26 @@ export default function Ui_Konfirmasi({ ); } - if (isJoin == false) { + // Jika belum join dan tanggal mulai acara belum lewat + if (isJoin == false && moment(data?.tanggal).diff(moment(), "minute") > 0) { return ( <> - + + ); + } + + // Jika belum join dan tanggal mulai acara sudah lewat + if (isJoin == false && moment(data?.tanggal).diff(moment(), "minute") < 0) { + return ( + <> + ; } @@ -126,16 +139,6 @@ export default function Ui_Konfirmasi({ /> ); } - // const tgl = moment(data?.tanggal).diff(moment(), "minute") < 0; - // return ( - // <> - // - // - // {JSON.stringify(tgl)} - // - // - // - // ); } function DataNotFound() { @@ -196,7 +199,91 @@ function SkeletonIsDataNull() { ); } -function UserNotJoin({ +function UserAllowToJoin({ + title, + tanggal, + lokasi, + eventId, + userLoginId, +}: { + title: string; + tanggal: Date; + lokasi: string; + eventId: string; + userLoginId: string; +}) { + const router = useRouter(); + const [isLoading, setLoading] = useState(false); + + async function onJoinEvent() { + setLoading(true); + + const data = { + userId: userLoginId, + eventId: eventId, + }; + + const res = await Event_funJoinEvent(data as any); + if (res.status === 200) { + ComponentGlobal_NotifikasiBerhasil(res.message, 2000); + router.push(RouterEvent.detail_main + eventId); + } else { + setLoading(false); + ComponentGlobal_NotifikasiGagal(res.message); + } + } + + return ( + <> + + + + + + Halo, Bapak/Ibu. Kami dengan senang hati mengundang Anda untuk + bergabung dalam acara + + {title} + {" "} + yang akan diselenggarakan pada{" "} + + {moment(tanggal).format("LL")} + {" "} + pukul + + {moment(tanggal).format("LT")} + {" "} + di + + {lokasi} + {" "} + . Pastikan Anda sudah melakukan registrasi melalui aplikasi + [Nama Aplikasi] agar dapat berpartisipasi. Kami sangat + menantikan kehadiran Anda. Sampai jumpa di acara ini. + + + + + + + + + ); +} + +function UserNotJoinAndEventReady({ title, eventId, userLoginId, @@ -373,8 +460,12 @@ function UserNotConfirm({ {title}
{" "} pada hari ini. Mohon untuk mengonfirmasi kehadiran Anda dengan - menekan tombol {"Hadir"} atau fitur konfirmasi yang tersedia di - bawah. Terima kasih dan selamat menikmati acara. + menekan tombol{" "} + + Konfirmasi Kehadiran + {" "} + atau fitur konfirmasi yang tersedia di bawah. Terima kasih dan + selamat menikmati acara. diff --git a/src/app_modules/event/component/detail/list_peserta.tsx b/src/app_modules/event/component/detail/list_peserta.tsx index a8c53851..ab93a45d 100644 --- a/src/app_modules/event/component/detail/list_peserta.tsx +++ b/src/app_modules/event/component/detail/list_peserta.tsx @@ -3,6 +3,7 @@ import { ActionIcon, Avatar, + Badge, Center, Grid, Group, @@ -136,11 +137,8 @@ function ComponentEvent_AvatarAndUsername({ } } - const tglMulai = moment(tanggalMulai).diff(moment(), "minutes"); + const tglMulai = moment(tanggalMulai).diff(moment(), "minutes") < 0; - const tglSelesai = moment(tanggalSelesai).diff(moment(), "minutes"); - - // console.log("mulai:", tglMulai, "selesai:", tglSelesai); return ( <> @@ -177,20 +175,12 @@ function ComponentEvent_AvatarAndUsername({ - {/* {component && ( - - - {component} - - - )} */} - - {tglMulai < 0 && ( - + {tglMulai && ( + - {isPresent ? "Hadir" : "-"} + {isPresent ? Hadir : -} diff --git a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx index 02bd0b77..cd7b5204 100644 --- a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx +++ b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx @@ -47,40 +47,46 @@ export function Profile_ComponentCreateNewProfile({ "Lengkapi background profile" ); - setLoading(true); + try { + setLoading(true); - const uploadPhoto = await funGlobal_UploadToStorage({ - file: filePP, - dirId: DIRECTORY_ID.profile_foto, - }); - if (!uploadPhoto.success) { - setLoading(false); - return ComponentGlobal_NotifikasiPeringatan("Gagal upload foto profile"); - } + const uploadPhoto = await funGlobal_UploadToStorage({ + file: filePP, + dirId: DIRECTORY_ID.profile_foto, + }); + if (!uploadPhoto.success) { + setLoading(false); + return ComponentGlobal_NotifikasiPeringatan( + "Gagal upload foto profile" + ); + } - const uploadBackground = await funGlobal_UploadToStorage({ - file: fileBG, - dirId: DIRECTORY_ID.profile_background, - }); - if (!uploadBackground.success) { - setLoading(false); - return ComponentGlobal_NotifikasiPeringatan( - "Gagal upload background profile" - ); - } + const uploadBackground = await funGlobal_UploadToStorage({ + file: fileBG, + dirId: DIRECTORY_ID.profile_background, + }); + if (!uploadBackground.success) { + setLoading(false); + return ComponentGlobal_NotifikasiPeringatan( + "Gagal upload background profile" + ); + } - const create = await funCreateNewProfile({ - data: newData as any, - imageId: uploadPhoto.data.id, - imageBackgroundId: uploadBackground.data.id, - }); + const create = await funCreateNewProfile({ + data: newData as any, + imageId: uploadPhoto.data.id, + imageBackgroundId: uploadBackground.data.id, + }); - if (create.status === 201) { - ComponentGlobal_NotifikasiBerhasil("Berhasil membuat profile", 3000); - router.push(RouterHome.main_home, { scroll: false }); - } else { - ComponentGlobal_NotifikasiGagal(create.message); - setLoading(false); + if (create.status === 201) { + ComponentGlobal_NotifikasiBerhasil("Berhasil membuat profile", 3000); + router.push(RouterHome.main_home, { scroll: false }); + } else { + ComponentGlobal_NotifikasiGagal(create.message); + setLoading(false); + } + } catch (error) { + console.log(error); } } From 2ae15e9f93ffd322b49b93ff5328174c7df59bf7 Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 9 Dec 2024 14:47:35 +0800 Subject: [PATCH 033/595] upd: lainnya Deskripsi: - api get all forum (blm jadi) - loading on bussines map No Issues --- src/app/api/new/forum/route.ts | 67 +++++++++++++++++++++++++++ src/app_modules/map/ui/ui_map_new.tsx | 7 ++- 2 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/app/api/new/forum/route.ts diff --git a/src/app/api/new/forum/route.ts b/src/app/api/new/forum/route.ts new file mode 100644 index 00000000..84d3523a --- /dev/null +++ b/src/app/api/new/forum/route.ts @@ -0,0 +1,67 @@ +import { prisma } from "@/app/lib" +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + +// GET ALL DATA PORTOFOLIO BY PROFILE ID +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url) + const page = searchParams.get("page") + const search = searchParams.get("search") + const dataSkip = Number(page) * 5 - 5; + + const data = await prisma.forum_Posting.findMany({ + take: 5, + skip: dataSkip, + orderBy: { + createdAt: "desc", + }, + where: { + isActive: true, + diskusi: { + mode: "insensitive", + contains: (search == undefined || search == "null") ? "" : search, + }, + }, + select: { + id: true, + diskusi: true, + createdAt: true, + isActive: true, + authorId: true, + Author: { + select: { + id: true, + username: true, + Profile: { + select: { + id: true, + name: true, + imageId: true, + }, + }, + }, + }, + Forum_Komentar: { + where: { + isActive: true, + }, + }, + ForumMaster_StatusPosting: { + select: { + id: true, + status: true, + }, + }, + forumMaster_StatusPostingId: 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_modules/map/ui/ui_map_new.tsx b/src/app_modules/map/ui/ui_map_new.tsx index 6797cee0..2ce61efc 100644 --- a/src/app_modules/map/ui/ui_map_new.tsx +++ b/src/app_modules/map/ui/ui_map_new.tsx @@ -2,7 +2,7 @@ import { APIs } from "@/app/lib"; import { AccentColor } from "@/app_modules/_global/color/color_pallet"; import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; -import { Avatar, Stack } from "@mantine/core"; +import { Avatar, Loader, Stack } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import "mapbox-gl/dist/mapbox-gl.css"; import { useState } from "react"; @@ -44,7 +44,10 @@ export function UiMap_MapBoxViewNew({ mapboxToken, }: { mapboxToken: string }) { { loading ? - <> : + + + + : Date: Mon, 9 Dec 2024 15:46:28 +0800 Subject: [PATCH 034/595] upd: katalog profile Deskripsi: - update bug button admin No Issues --- .../katalog/component/drawer_katalog_new.tsx | 2 +- src/app_modules/katalog/main/layout_new.tsx | 20 ++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/app_modules/katalog/component/drawer_katalog_new.tsx b/src/app_modules/katalog/component/drawer_katalog_new.tsx index c0ef291f..9e154560 100644 --- a/src/app_modules/katalog/component/drawer_katalog_new.tsx +++ b/src/app_modules/katalog/component/drawer_katalog_new.tsx @@ -89,7 +89,7 @@ export default function DrawerKatalogNew({ opened, close, userRoleId, userId }: ))} - {userRoleId != "1" && ( + {userRoleId != "1" && userRoleId != "" && ( - ) : ( - open()}> - - - ) + loading ? + : + authorId == userLoginId ? ( + open()}> + + + ) : ( + + ) } /> } From ead4913514c9da8388e6d841f20a01fc02f78756 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 9 Dec 2024 17:29:32 +0800 Subject: [PATCH 035/595] Fix event Deskripsi: - Fix konfirmasi event --- src/app_modules/event/_ui/konfirmasi.tsx | 67 ++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/src/app_modules/event/_ui/konfirmasi.tsx b/src/app_modules/event/_ui/konfirmasi.tsx index 2062d1b0..bc243cc1 100644 --- a/src/app_modules/event/_ui/konfirmasi.tsx +++ b/src/app_modules/event/_ui/konfirmasi.tsx @@ -20,6 +20,7 @@ import { Event_funJoinAndConfirmEvent } from "../fun/create/fun_join_and_confirm import { gs_event_hotMenu } from "../global_state"; import { MODEL_EVENT } from "../model/interface"; import { Event_funJoinEvent } from "../fun/create/fun_join_event"; +import "moment/locale/id"; export default function Ui_Konfirmasi({ userLoginId, @@ -88,7 +89,7 @@ export default function Ui_Konfirmasi({ ); } - // Jika tanggal acara lewat + // Jika tanggal acara sudah lewat if (moment(data?.tanggalSelesai).diff(moment(), "minute") < 0) { return ( <> @@ -97,6 +98,15 @@ export default function Ui_Konfirmasi({ ); } + // Jika join true + if (isJoin == true && moment(data?.tanggal).diff(moment(), "minute") > 0) { + return ( + <> + + + ); + } + // Jika belum join dan tanggal mulai acara belum lewat if (isJoin == false && moment(data?.tanggal).diff(moment(), "minute") > 0) { return ( @@ -125,8 +135,13 @@ export default function Ui_Konfirmasi({ ); } - // Jika sudah join, belum konfirm dan acara sudah mulai - if (isPresent && moment(data?.tanggal).diff(moment(), "minute") < 0) { + // Jika sudah join, sudah konfirmasi dan tanggal mulai acara sudah lewat + // if (isPresent && moment(data?.tanggal).diff(moment(), "minute") < 0) + if ( + isPresent && + isJoin && + moment(data?.tanggal).diff(moment(), "minute") < 0 + ) { return ; } @@ -199,6 +214,52 @@ function SkeletonIsDataNull() { ); } +function UserJoinTrue({ title, tanggal }: { title: string; tanggal: Date }) { + const router = useRouter(); + const [isLoading, setLoading] = useState(false); + const [hotMenu, setHotMenu] = useAtom(gs_event_hotMenu); + + return ( + <> + + + + + + Terima kasih, Bapak/Ibu, Anda telah berhasil bergabung dalam + acara{" "} + + {title} + {" "} + . Mohon ditunggu hingga tanggal{" "} + + {moment(tanggal).format("DD-MM-YYYY")} + {" "} + untuk melakukan konfirmasi kehadiran melalui aplikasi HIPMI APP. + + + + + + + + + ); +} + function UserAllowToJoin({ title, tanggal, From a48a9ca99d05b91613db5d30dc4ae47d9a7740ed Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 9 Dec 2024 17:29:56 +0800 Subject: [PATCH 036/595] chore(release): 1.2.21 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbad195b..73e2314d 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.21](https://github.com/bipproduction/hipmi/compare/v1.2.20...v1.2.21) (2024-12-09) + ## [1.2.20](https://github.com/bipproduction/hipmi/compare/v1.2.19...v1.2.20) (2024-12-09) ## [1.2.19](https://github.com/bipproduction/hipmi/compare/v1.2.18...v1.2.19) (2024-12-06) diff --git a/package.json b/package.json index b47b092a..7f57f862 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.20", + "version": "1.2.21", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From bfddf58caaaed1e5573a080c865e0614d9e433ad Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 9 Dec 2024 17:30:30 +0800 Subject: [PATCH 037/595] Fix version 1.2.21 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7f57f862..ae8b1fa2 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" - }, + }, "scripts": { "dev": "bun --bun run next dev --experimental-https", "build": "NODE_OPTIONS='--max-old-space-size=2048' bun --bun run next build", From d166b1b9b68e891e677a23f7ec54d0b985b17983 Mon Sep 17 00:00:00 2001 From: amel Date: Tue, 10 Dec 2024 10:52:54 +0800 Subject: [PATCH 038/595] upd: waktu splash --- src/app_modules/colab/splash/index.tsx | 2 +- src/app_modules/crowd/splash/view.tsx | 2 +- src/app_modules/forum/splash/index.tsx | 2 +- src/app_modules/map/ui/ui_splash.tsx | 2 +- src/app_modules/vote/splash/index.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app_modules/colab/splash/index.tsx b/src/app_modules/colab/splash/index.tsx index 48839abb..3d6ee713 100644 --- a/src/app_modules/colab/splash/index.tsx +++ b/src/app_modules/colab/splash/index.tsx @@ -17,7 +17,7 @@ export default function Colab_Splash() { setHotMenu(1); // setStatus("Publish"); router.replace(RouterColab.beranda, {scroll: false}); - }, 1000); + }, 500); }, []); return ( diff --git a/src/app_modules/crowd/splash/view.tsx b/src/app_modules/crowd/splash/view.tsx index 65e4e84f..e60ab74a 100644 --- a/src/app_modules/crowd/splash/view.tsx +++ b/src/app_modules/crowd/splash/view.tsx @@ -10,7 +10,7 @@ export default function SplashCrowd() { const router = useRouter(); useShallowEffect(() => { - setTimeout(() => router.push(RouterCrowd.main), 1000); + setTimeout(() => router.push(RouterCrowd.main), 500); }, []); return ( <> diff --git a/src/app_modules/forum/splash/index.tsx b/src/app_modules/forum/splash/index.tsx index 205abb33..49d9eda6 100644 --- a/src/app_modules/forum/splash/index.tsx +++ b/src/app_modules/forum/splash/index.tsx @@ -11,7 +11,7 @@ export default function Forum_Splash() { useShallowEffect(() => { setTimeout(() => { router.replace(RouterForum.beranda, { scroll: false }); - }, 1000); + }, 500); }, []); return ( diff --git a/src/app_modules/map/ui/ui_splash.tsx b/src/app_modules/map/ui/ui_splash.tsx index d945b96c..607f58b8 100644 --- a/src/app_modules/map/ui/ui_splash.tsx +++ b/src/app_modules/map/ui/ui_splash.tsx @@ -11,7 +11,7 @@ export function UiMap_SplashView() { useShallowEffect(() => { setTimeout(() => { - router.replace(RouterMap.main_view, { scroll: false }), 1000; + router.replace(RouterMap.main_view, { scroll: false }), 500; }); }, []); return ( diff --git a/src/app_modules/vote/splash/index.tsx b/src/app_modules/vote/splash/index.tsx index 17a90c19..5a348b18 100644 --- a/src/app_modules/vote/splash/index.tsx +++ b/src/app_modules/vote/splash/index.tsx @@ -24,7 +24,7 @@ export default function Vote_Splash() { setTabsStatus("Publish"); setTabsRiwayat("Semua"); router.replace(RouterVote.beranda); - }, 1000); + }, 500); }, []); return ( From 8f5acae10d51484bc8fefc3272a0b7c10b297b83 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 10 Dec 2024 11:06:00 +0800 Subject: [PATCH 039/595] chore(release): 1.2.22 --- CHANGELOG.md | 2 ++ package.json | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73e2314d..75c82718 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.22](https://github.com/bipproduction/hipmi/compare/v1.2.21...v1.2.22) (2024-12-10) + ## [1.2.21](https://github.com/bipproduction/hipmi/compare/v1.2.20...v1.2.21) (2024-12-09) ## [1.2.20](https://github.com/bipproduction/hipmi/compare/v1.2.19...v1.2.20) (2024-12-09) diff --git a/package.json b/package.json index ae8b1fa2..5440fb7a 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "hipmi", - "version": "1.2.21", + "version": "1.2.22", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" - }, + }, "scripts": { "dev": "bun --bun run next dev --experimental-https", "build": "NODE_OPTIONS='--max-old-space-size=2048' bun --bun run next build", From b2e48ba06c5fcdb6d1543fa85342c1921d29e8db Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 10 Dec 2024 11:11:45 +0800 Subject: [PATCH 040/595] Fix create profile --- .../event/table_status/table_publish.tsx | 2 +- src/app_modules/event/_ui/konfirmasi.tsx | 20 +++--- .../event/component/detail/list_peserta.tsx | 72 ++++++++++++++----- .../button/comp_create_new_profile.tsx | 52 ++++++++------ 4 files changed, 96 insertions(+), 50 deletions(-) diff --git a/src/app_modules/admin/event/table_status/table_publish.tsx b/src/app_modules/admin/event/table_status/table_publish.tsx index 7bcd290b..1553c712 100644 --- a/src/app_modules/admin/event/table_status/table_publish.tsx +++ b/src/app_modules/admin/event/table_status/table_publish.tsx @@ -54,7 +54,7 @@ function TableStatus({ listPublish }: { listPublish: any }) { useShallowEffect(() => { if (typeof window !== "undefined") { - console.log(window.location.origin); + // console.log(window.location.origin); setOrigin(window.location.origin); } }, [setOrigin]); diff --git a/src/app_modules/event/_ui/konfirmasi.tsx b/src/app_modules/event/_ui/konfirmasi.tsx index bc243cc1..ecaa99df 100644 --- a/src/app_modules/event/_ui/konfirmasi.tsx +++ b/src/app_modules/event/_ui/konfirmasi.tsx @@ -135,16 +135,6 @@ export default function Ui_Konfirmasi({ ); } - // Jika sudah join, sudah konfirmasi dan tanggal mulai acara sudah lewat - // if (isPresent && moment(data?.tanggal).diff(moment(), "minute") < 0) - if ( - isPresent && - isJoin && - moment(data?.tanggal).diff(moment(), "minute") < 0 - ) { - return ; - } - if (isPresent == false && data) { return ( ); } + + // Jika sudah join, sudah konfirmasi dan tanggal mulai acara sudah lewat + // if (isPresent && moment(data?.tanggal).diff(moment(), "minute") < 0) + if ( + isPresent && + isJoin && + moment(data?.tanggal).diff(moment(), "minute") < 0 + ) { + return ; + } } function DataNotFound() { diff --git a/src/app_modules/event/component/detail/list_peserta.tsx b/src/app_modules/event/component/detail/list_peserta.tsx index ab93a45d..0089b2f2 100644 --- a/src/app_modules/event/component/detail/list_peserta.tsx +++ b/src/app_modules/event/component/detail/list_peserta.tsx @@ -7,6 +7,7 @@ import { Center, Grid, Group, + Loader, Skeleton, Stack, Text, @@ -31,6 +32,8 @@ import moment from "moment"; import { useShallowEffect } from "@mantine/hooks"; import { API_RouteEvent } from "@/app/lib/api_user_router/route_api_event"; import Event_ComponentSkeletonListPeserta from "../skeleton/comp_skeleton_list_peserta"; +import { ScrollOnly } from "next-scroll-loader"; +import { event_newGetListPesertaById } from "../../fun"; export default function ComponentEvent_ListPeserta({ total, @@ -43,6 +46,7 @@ export default function ComponentEvent_ListPeserta({ }) { const router = useRouter(); const [data, setData] = useState(null); + const [activePage, setActivePage] = useState(1); useShallowEffect(() => { onLoadPeserta(); @@ -80,22 +84,53 @@ export default function ComponentEvent_ListPeserta({
) : ( - - {data.map((e, i) => ( - - + // + // {data.map((e, i) => ( + // + // - {/* */} - - ))} - + // {/* */} + // + // ))} + // + ( +
+ +
+ )} + data={data} + setData={setData as any} + moreData={async () => { + const loadData = await event_newGetListPesertaById({ + eventId: eventId as string, + page: activePage + 1, + }); + + setActivePage((val) => val + 1); + + return loadData; + }} + > + {(item) => ( + + )} +
)}
@@ -139,7 +174,6 @@ function ComponentEvent_AvatarAndUsername({ const tglMulai = moment(tanggalMulai).diff(moment(), "minutes") < 0; - return ( <> @@ -180,7 +214,11 @@ function ComponentEvent_AvatarAndUsername({ - {isPresent ? Hadir : -} + {isPresent ? ( + Hadir + ) : ( + - + )} diff --git a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx index cd7b5204..2ac6621b 100644 --- a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx +++ b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx @@ -54,6 +54,7 @@ export function Profile_ComponentCreateNewProfile({ file: filePP, dirId: DIRECTORY_ID.profile_foto, }); + // console.log("ini foto", uploadPhoto); if (!uploadPhoto.success) { setLoading(false); return ComponentGlobal_NotifikasiPeringatan( @@ -61,29 +62,36 @@ export function Profile_ComponentCreateNewProfile({ ); } - const uploadBackground = await funGlobal_UploadToStorage({ - file: fileBG, - dirId: DIRECTORY_ID.profile_background, - }); - if (!uploadBackground.success) { - setLoading(false); - return ComponentGlobal_NotifikasiPeringatan( - "Gagal upload background profile" - ); - } + if (uploadPhoto.success) { + const uploadBackground = await funGlobal_UploadToStorage({ + file: fileBG, + dirId: DIRECTORY_ID.profile_background, + }); + // console.log("ini background", uploadBackground); + if (!uploadBackground.success) { + setLoading(false); + return ComponentGlobal_NotifikasiPeringatan( + "Gagal upload background profile" + ); + } - const create = await funCreateNewProfile({ - data: newData as any, - imageId: uploadPhoto.data.id, - imageBackgroundId: uploadBackground.data.id, - }); - - if (create.status === 201) { - ComponentGlobal_NotifikasiBerhasil("Berhasil membuat profile", 3000); - router.push(RouterHome.main_home, { scroll: false }); - } else { - ComponentGlobal_NotifikasiGagal(create.message); - setLoading(false); + if (uploadBackground.success) { + const create = await funCreateNewProfile({ + data: newData as any, + imageId: uploadPhoto.data.id, + imageBackgroundId: uploadBackground.data.id, + }); + if (create.status === 201) { + ComponentGlobal_NotifikasiBerhasil( + "Berhasil membuat profile", + 3000 + ); + router.push(RouterHome.main_home, { scroll: false }); + } else { + ComponentGlobal_NotifikasiGagal(create.message); + setLoading(false); + } + } } } catch (error) { console.log(error); From 6e1a2e4bd3f78acd50ed737d4f6c97022b4acba9 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Wed, 11 Dec 2024 10:32:00 +0800 Subject: [PATCH 041/595] Fix upload Deksripsi: - Fix upload foto - Fix email regex --- bun.lockb | Bin 703886 -> 708596 bytes package.json | 2 + src/app/api/auth/register/route.ts | 65 ++--- src/app/api/auth/validasi/route.ts | 33 +-- src/app/api/upload/route.ts | 51 ++++ src/app/zCoba/page.tsx | 109 +++++++- .../fun/upload/fun_upload_to_storage.ts | 2 - src/app_modules/auth/register/view.tsx | 10 +- .../katalog/component/drawer_katalog_new.tsx | 239 +++++++++++------- .../katalog/component/regular_expressions.ts | 1 + .../button/comp_create_new_profile.tsx | 53 ++-- .../katalog/profile/create/view.tsx | 29 ++- src/middleware.ts | 1 + 13 files changed, 376 insertions(+), 219 deletions(-) create mode 100644 src/app/api/upload/route.ts diff --git a/bun.lockb b/bun.lockb index 03d96215d1f03c487d9cdac0e7d7823cb0eee742..88eaa6ad3e0e2d12a92a93c78751e54fe7ad9b20 100755 GIT binary patch delta 125612 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 At^fc4 delta 122923 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

{ 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 diff --git a/package.json b/package.json index 5440fb7a..63e0b3b0 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@tiptap/pm": "^2.2.3", "@tiptap/react": "^2.2.3", "@tiptap/starter-kit": "^2.2.3", + "@types/bun": "^1.1.14", "@types/lodash": "^4.17.4", "@types/mapbox-gl": "^3.4.0", "@types/node": "20.4.5", @@ -48,6 +49,7 @@ "@types/uuid": "^9.0.4", "autoprefixer": "10.4.14", "bufferutil": "^4.0.8", + "bun": "^1.1.38", "dayjs": "^1.11.10", "dotenv": "^16.4.5", "echarts": "^5.4.3", diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts index 45d3cd9e..c307d724 100644 --- a/src/app/api/auth/register/route.ts +++ b/src/app/api/auth/register/route.ts @@ -12,47 +12,34 @@ export async function POST(req: Request) { }, }); - if (cekUsername) + try { + if (cekUsername) + return NextResponse.json( + { success: false, message: "Username sudah digunakan" }, + { status: 400 } + ); + + const createUser = await prisma.user.create({ + data: { + username: data.username, + nomor: data.nomor, + active: true, + }, + }); + + const token = await sessionCreate({ + sessionKey: process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, + encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, + user: createUser as any, + }); + return NextResponse.json( - { success: false, message: "Username sudah digunakan" }, - { status: 400 } + { success: true, message: "Berhasil Login", data: createUser }, + { status: 200 } ); - - const createUser = await prisma.user.create({ - data: { - username: data.username, - nomor: data.nomor, - active: true, - }, - }); - - const token = await sessionCreate({ - sessionKey: process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, - encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, - user: createUser as any, - }); - - // try { - // const createUserSession = await prisma.userSession.create({ - // data: { - // token: token as string, - // userId: createUser.id, - // }, - // }); - - // if (!createUserSession) - // return NextResponse.json( - // { success: false, message: "Gagal Membuat Session" }, - // { status: 400 } - // ); - // } catch (error) { - // console.log(error); - // } - - return NextResponse.json( - { success: true, message: "Berhasil Login", data: createUser }, - { status: 200 } - ); + } catch (error) { + console.log(error); + } } return NextResponse.json( diff --git a/src/app/api/auth/validasi/route.ts b/src/app/api/auth/validasi/route.ts index 05ee6b26..69c64a8c 100644 --- a/src/app/api/auth/validasi/route.ts +++ b/src/app/api/auth/validasi/route.ts @@ -20,7 +20,7 @@ export async function POST(req: Request) { }, }); - if (dataUser === null) + if (dataUser == null) return NextResponse.json( { success: false, message: "Nomor Belum Terdaftar" }, { status: 404 } @@ -32,37 +32,6 @@ export async function POST(req: Request) { user: dataUser as any, }); - // const cekSessionUser = await prisma.userSession.findFirst({ - // where: { - // userId: dataUser.id, - // }, - // }); - - // if (cekSessionUser !== null) { - // await prisma.userSession.delete({ - // where: { - // userId: dataUser.id, - // }, - // }); - // } - - // try { - // const createUserSession = await prisma.userSession.create({ - // data: { - // token: token as string, - // userId: dataUser.id, - // }, - // }); - - // if (!createUserSession) - // return NextResponse.json( - // { success: false, message: "Gagal Membuat Session" }, - // { status: 400 } - // ); - // } catch (error) { - // console.log(error); - // } - return NextResponse.json( { success: true, diff --git a/src/app/api/upload/route.ts b/src/app/api/upload/route.ts new file mode 100644 index 00000000..00e2b69c --- /dev/null +++ b/src/app/api/upload/route.ts @@ -0,0 +1,51 @@ +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/zCoba/page.tsx b/src/app/zCoba/page.tsx index f07fb56c..8df38920 100644 --- a/src/app/zCoba/page.tsx +++ b/src/app/zCoba/page.tsx @@ -1,18 +1,111 @@ +"use client"; + +import { MainColor } from "@/app_modules/_global/color"; import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; import Coba_TestLoading from "@/app_modules/zCoba"; +import { + Avatar, + Button, + Center, + FileButton, + Paper, + Stack, +} from "@mantine/core"; +import { IconCamera } from "@tabler/icons-react"; +import { useState } from "react"; +import { DIRECTORY_ID } from "../lib"; +import { TokenStorage } from "../lib/token"; +import { envs } from "@/lib/envs"; -export default async function Page() { - await new Promise((a, b) => { - setTimeout(a, 3000); - }); +export default function Page() { + const [filePP, setFilePP] = useState(null); + const [imgPP, setImgPP] = useState(); - const userLoginId = await funGetUserIdByToken(); + async function onSave() { + const body = { + file: filePP, + dirId: DIRECTORY_ID.profile_foto, + }; + + const token = + "QWERTYUIOPLKJHGFDSAZXCVBNMQAZWSXEDCRFVTGBYHNUJMIKOLPPOIUYTREWQLKJHGFDSAMNBVCXZlghvftyguhijknhbgvcfytguu8okjnhbgvfty7u8oilkjnhgvtygu7u8ojilnkhbgvhujnkhghvjhukjnhb"; + + const formData = new FormData(); + formData.append("file", filePP as any); + + const res = await fetch("/api/upload", { + method: "POST", + body: formData, + }); + + console.log(await res.json()); + } return ( <> - {/* */} - - {/* */} + +

+ {imgPP ? ( + + + + ) : ( + + + + )} +
+ + { + try { + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + setImgPP(buffer); + setFilePP(files); + } catch (error) { + console.log(error); + } + }} + accept="image/png,image/jpeg" + > + {(props) => ( + + )} + + + + ); } 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 30bc8a51..52155b9b 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 @@ -54,6 +54,4 @@ export async function funGlobal_UploadToStorage({ console.error("Error:", error); return { success: false, data: {} }; } - - return { success: false, data: { id: "" } }; } diff --git a/src/app_modules/auth/register/view.tsx b/src/app_modules/auth/register/view.tsx index f72d7e50..a8b6f985 100644 --- a/src/app_modules/auth/register/view.tsx +++ b/src/app_modules/auth/register/view.tsx @@ -64,6 +64,11 @@ export default function Register() { const result = await res.json(); + if (res.status === 400) { + setLoading(false); + ComponentGlobal_NotifikasiPeringatan(result.message); + } + if (res.status === 200) { localStorage.removeItem("hipmi_auth_code_id"); ComponentGlobal_NotifikasiBerhasil(result.message); @@ -73,11 +78,6 @@ export default function Register() { nomor: data.nomor, }); } - - if (res.status === 400) { - setLoading(false); - ComponentGlobal_NotifikasiPeringatan(result.message); - } } catch (error) { console.log(error); } diff --git a/src/app_modules/katalog/component/drawer_katalog_new.tsx b/src/app_modules/katalog/component/drawer_katalog_new.tsx index 9e154560..752a7643 100644 --- a/src/app_modules/katalog/component/drawer_katalog_new.tsx +++ b/src/app_modules/katalog/component/drawer_katalog_new.tsx @@ -1,110 +1,153 @@ import { RouterAdminDashboard } from "@/app/lib/router_hipmi/router_admin"; -import { RouterPortofolio, RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; +import { + RouterPortofolio, + RouterProfile, +} from "@/app/lib/router_hipmi/router_katalog"; import { AccentColor } from "@/app_modules/_global/color"; +import { + gs_admin_navbar_menu, + gs_admin_navbar_subMenu, +} from "@/app_modules/admin/_admin_global/new_global_state"; import Component_ButtonLogout from "@/app_modules/auth/logout/view"; -import { ActionIcon, Drawer, Group, SimpleGrid, Stack, Text } from "@mantine/core"; +import { + ActionIcon, + Drawer, + Group, + SimpleGrid, + Stack, + Text, +} from "@mantine/core"; import { IconDashboard } from "@tabler/icons-react"; -import { IconEdit, IconPencilPlus, IconPhotoEdit, IconPolaroid, IconX } from "@tabler/icons-react"; +import { + IconEdit, + IconPencilPlus, + IconPhotoEdit, + IconPolaroid, + IconX, +} from "@tabler/icons-react"; +import { useAtom } from "jotai"; import { useParams, useRouter } from "next/navigation"; -export default function DrawerKatalogNew({ opened, close, userRoleId, userId }: { opened: boolean, close: () => void, userRoleId: string, userId: string }) { - const param = useParams<{ id: string }>() - const router = useRouter() +export default function DrawerKatalogNew({ + opened, + close, + userRoleId, + userId, +}: { + opened: boolean; + close: () => void; + userRoleId: string; + userId: string; +}) { + const param = useParams<{ id: string }>(); + const router = useRouter(); + const [activeId, setActiveId] = useAtom(gs_admin_navbar_menu); + const [activeChildId, setActiveChildId] = useAtom(gs_admin_navbar_subMenu); - const listPage = [ - { - id: "1", - name: "Edit profile", - icon: , - path: RouterProfile.edit + param.id, - }, - { - id: "2", - name: "Ubah foto profile", - icon: , - path: RouterProfile.update_foto_profile + param.id, - }, - { - id: "3", - name: "Ubah latar belakang", - icon: , - path: RouterProfile.update_foto_background + param.id, - }, - { - id: "4", - name: "Tambah portofolio", - icon: , - path: RouterPortofolio.create + param.id, - }, - ]; + const listPage = [ + { + id: "1", + name: "Edit profile", + icon: , + path: RouterProfile.edit + param.id, + }, + { + id: "2", + name: "Ubah foto profile", + icon: , + path: RouterProfile.update_foto_profile + param.id, + }, + { + id: "3", + name: "Ubah latar belakang", + icon: , + path: RouterProfile.update_foto_background + param.id, + }, + { + id: "4", + name: "Tambah portofolio", + icon: , + path: RouterPortofolio.create + param.id, + }, + ]; - return <> + return ( + <> close()} - position={"bottom"} - size={"auto"} - withCloseButton={false} - styles={{ - content: { - padding: 0, - position: "absolute", - margin: "auto", - backgroundColor: "transparent", - left: 0, - right: 0, - width: 500, - }, - body: { - backgroundColor: AccentColor.darkblue, - borderTop: `2px solid ${AccentColor.blue}`, - borderRight: `1px solid ${AccentColor.blue}`, - borderLeft: `1px solid ${AccentColor.blue}`, - borderRadius: "20px 20px 0px 0px", - color: "white", - paddingBottom: "5%", - }, - }} + opened={opened} + onClose={() => close()} + position={"bottom"} + size={"auto"} + withCloseButton={false} + styles={{ + content: { + padding: 0, + position: "absolute", + margin: "auto", + backgroundColor: "transparent", + left: 0, + right: 0, + width: 500, + }, + body: { + backgroundColor: AccentColor.darkblue, + borderTop: `2px solid ${AccentColor.blue}`, + borderRight: `1px solid ${AccentColor.blue}`, + borderLeft: `1px solid ${AccentColor.blue}`, + borderRadius: "20px 20px 0px 0px", + color: "white", + paddingBottom: "5%", + }, + }} > - - - - - - - - {listPage.map((e, i) => ( - - { router.push(e.path, { scroll: false }); }} - > - {e.icon} - - - {e.name} - - - ))} + + + + + + + + {listPage.map((e, i) => ( + + { + router.push(e.path, { scroll: false }); + }} + > + {e.icon} + + + {e.name} + + + ))} - - {userRoleId != "1" && userRoleId != "" && ( - - { router.push(RouterAdminDashboard.main_admin, { scroll: false }); }} - > - - - - Dashboard Admin - - - )} - - + + {userRoleId != "1" && userRoleId != "" && ( + + { + setActiveId("Main"); + setActiveChildId(""); + router.push(RouterAdminDashboard.main_admin, { + scroll: false, + }); + }} + > + + + + Dashboard Admin + + + )} + + - ; -} \ No newline at end of file + + ); +} diff --git a/src/app_modules/katalog/component/regular_expressions.ts b/src/app_modules/katalog/component/regular_expressions.ts index 526ff127..bb809be0 100644 --- a/src/app_modules/katalog/component/regular_expressions.ts +++ b/src/app_modules/katalog/component/regular_expressions.ts @@ -1,2 +1,3 @@ export var validRegex = /^([a-zA-Z0-9\.!#$%&'*+/=?^_`{|}~-]+)@([a-zA-Z0-9])+.([a-z]+)(.[a-z]+)?$/; +export const gmailRegex = /^[a-zA-Z0-9._%+-]+@gmail\.com$/; \ No newline at end of file diff --git a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx index 2ac6621b..e523a2ba 100644 --- a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx +++ b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx @@ -16,6 +16,8 @@ import { useState } from "react"; import funCreateNewProfile from "../../fun/fun_create_profile"; import { MODEL_PROFILE } from "../../model/interface"; import { validRegex } from "@/app_modules/katalog/component"; +import { envs } from "@/lib/envs"; +import { TokenProvider, TokenStorage } from "@/app/lib/token"; export function Profile_ComponentCreateNewProfile({ value, @@ -54,7 +56,6 @@ export function Profile_ComponentCreateNewProfile({ file: filePP, dirId: DIRECTORY_ID.profile_foto, }); - // console.log("ini foto", uploadPhoto); if (!uploadPhoto.success) { setLoading(false); return ComponentGlobal_NotifikasiPeringatan( @@ -62,36 +63,28 @@ export function Profile_ComponentCreateNewProfile({ ); } - if (uploadPhoto.success) { - const uploadBackground = await funGlobal_UploadToStorage({ - file: fileBG, - dirId: DIRECTORY_ID.profile_background, - }); - // console.log("ini background", uploadBackground); - if (!uploadBackground.success) { - setLoading(false); - return ComponentGlobal_NotifikasiPeringatan( - "Gagal upload background profile" - ); - } + const uploadBackground = await funGlobal_UploadToStorage({ + file: fileBG, + dirId: DIRECTORY_ID.profile_background, + }); + if (!uploadBackground.success) { + setLoading(false); + return ComponentGlobal_NotifikasiPeringatan( + "Gagal upload background profile" + ); + } - if (uploadBackground.success) { - const create = await funCreateNewProfile({ - data: newData as any, - imageId: uploadPhoto.data.id, - imageBackgroundId: uploadBackground.data.id, - }); - if (create.status === 201) { - ComponentGlobal_NotifikasiBerhasil( - "Berhasil membuat profile", - 3000 - ); - router.push(RouterHome.main_home, { scroll: false }); - } else { - ComponentGlobal_NotifikasiGagal(create.message); - setLoading(false); - } - } + const create = await funCreateNewProfile({ + data: newData as any, + imageId: uploadPhoto.data.id, + imageBackgroundId: uploadBackground.data.id, + }); + if (create.status === 201) { + ComponentGlobal_NotifikasiBerhasil("Berhasil membuat profile", 3000); + router.push(RouterHome.main_home, { scroll: false }); + } else { + ComponentGlobal_NotifikasiGagal(create.message); + setLoading(false); } } catch (error) { console.log(error); diff --git a/src/app_modules/katalog/profile/create/view.tsx b/src/app_modules/katalog/profile/create/view.tsx index 0ae5e658..c97942ee 100644 --- a/src/app_modules/katalog/profile/create/view.tsx +++ b/src/app_modules/katalog/profile/create/view.tsx @@ -24,6 +24,8 @@ import { IconAt, IconCamera, IconUpload } from "@tabler/icons-react"; import { useState } from "react"; import { validRegex } from "../../component"; import { Profile_ComponentCreateNewProfile } from "../_component"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { gmailRegex } from "../../component/regular_expressions"; export default function CreateProfile() { const [filePP, setFilePP] = useState(null); @@ -38,6 +40,9 @@ export default function CreateProfile() { jenisKelamin: "", }); + // Maksimal ukuran file dalam byte (2 MB) + const MAX_SIZE = 2 * 1024 * 1024; // 2 MB + return ( <> @@ -83,8 +88,15 @@ export default function CreateProfile() { const buffer = URL.createObjectURL( new Blob([new Uint8Array(await files.arrayBuffer())]) ); - setImgPP(buffer); - setFilePP(files); + + if (files.size > MAX_SIZE) { + ComponentGlobal_NotifikasiPeringatan( + "Ukuran file terlalu besar. Maksimal 2 MB." + ); + } else { + setImgPP(buffer); + setFilePP(files); + } } catch (error) { console.log(error); } @@ -138,8 +150,15 @@ export default function CreateProfile() { const buffer = URL.createObjectURL( new Blob([new Uint8Array(await files.arrayBuffer())]) ); - setImgBG(buffer); - setFileBG(files); + + if (files.size > MAX_SIZE) { + ComponentGlobal_NotifikasiPeringatan( + "Ukuran file terlalu besar. Maksimal 2 MB." + ); + } else { + setImgBG(buffer); + setFileBG(files); + } } catch (error) { console.log(error); } @@ -189,7 +208,7 @@ export default function CreateProfile() { maxLength={100} placeholder="Contoh: User@gmail.com" error={ - value.email.length > 0 && !value.email.match(validRegex) ? ( + value.email.length > 0 && !value.email.match(gmailRegex) ? ( ) : ( "" diff --git a/src/middleware.ts b/src/middleware.ts index 621bde58..f4cd23ab 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -19,6 +19,7 @@ const middlewareConfig: MiddlewareConfig = { userPath: "/dev/home", publicRoutes: [ "/", + "/api/upload", "/api/validation", "/api/auth/*", "/api/origin-url", From 67a66edd19040cd0de4a5df86d580014f63b9fd9 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Wed, 11 Dec 2024 11:00:46 +0800 Subject: [PATCH 042/595] chore(release): 1.2.23 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75c82718..62e0291c 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.23](https://github.com/bipproduction/hipmi/compare/v1.2.22...v1.2.23) (2024-12-11) + ## [1.2.22](https://github.com/bipproduction/hipmi/compare/v1.2.21...v1.2.22) (2024-12-10) ## [1.2.21](https://github.com/bipproduction/hipmi/compare/v1.2.20...v1.2.21) (2024-12-09) diff --git a/package.json b/package.json index 63e0b3b0..68de6a42 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.22", + "version": "1.2.23", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 97fe458e1dede3f2faaeb921c44cdc20d3ec25be Mon Sep 17 00:00:00 2001 From: amel Date: Wed, 11 Dec 2024 11:51:34 +0800 Subject: [PATCH 043/595] upd: button back Deskripsi: - ilangin loading button back No s No Issues --- src/app_modules/_global/ui/ui_header_tamplate.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/app_modules/_global/ui/ui_header_tamplate.tsx b/src/app_modules/_global/ui/ui_header_tamplate.tsx index 6126711c..6f6425e9 100644 --- a/src/app_modules/_global/ui/ui_header_tamplate.tsx +++ b/src/app_modules/_global/ui/ui_header_tamplate.tsx @@ -77,12 +77,21 @@ export default function UIGlobal_LayoutHeaderTamplate({ : router.push(routerLeft, { scroll: false }); }} > - {isLoading ? ( + {/* PAKE LOADING SAAT KLIK BACK */} + {/* {isLoading ? ( ) : iconLeft ? ( iconLeft ) : ( + )} */} + + + {/* GA PAKE LOADING SAAT KLIK BACK */} + {iconLeft ? ( + iconLeft + ) : ( + )} )} From eab293dd35a6f061cfa4675679de1ce68f506c3c Mon Sep 17 00:00:00 2001 From: amel Date: Wed, 11 Dec 2024 12:03:56 +0800 Subject: [PATCH 044/595] fix: drawer katalog Deskripsi: - on click kotak drawer No Issues --- .../katalog/component/drawer_katalog_new.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/app_modules/katalog/component/drawer_katalog_new.tsx b/src/app_modules/katalog/component/drawer_katalog_new.tsx index 752a7643..ab0ef95c 100644 --- a/src/app_modules/katalog/component/drawer_katalog_new.tsx +++ b/src/app_modules/katalog/component/drawer_katalog_new.tsx @@ -108,14 +108,12 @@ export default function DrawerKatalogNew({ {listPage.map((e, i) => ( - - { - router.push(e.path, { scroll: false }); - }} - > + { + router.push(e.path, { scroll: false }); + }} + > + {e.icon} From 9ceb4867e35b991449a7f64995d63096908fd02f Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Wed, 11 Dec 2024 13:58:32 +0800 Subject: [PATCH 045/595] Fix profile Deskripsi: - Fix load image saat di buka --- src/app/dev/profile/edit/[id]/page.tsx | 6 ++---- .../_global/button/comp_button_upload_photo.tsx | 12 ++++++++++-- src/app_modules/_global/lib/index.ts | 3 +++ src/app_modules/_global/lib/max_size.ts | 2 ++ src/app_modules/_global/ui/ui_image_preview.tsx | 2 +- .../_component/button/comp_create_new_profile.tsx | 7 +++---- src/app_modules/katalog/profile/create/view.tsx | 9 +++------ src/app_modules/katalog/profile/edit/view.tsx | 12 +++++++----- .../katalog/profile/upload/foto_background/index.tsx | 5 ++++- .../katalog/profile/upload/foto_profile/index.tsx | 6 +++++- 10 files changed, 40 insertions(+), 24 deletions(-) create mode 100644 src/app_modules/_global/lib/index.ts create mode 100644 src/app_modules/_global/lib/max_size.ts diff --git a/src/app/dev/profile/edit/[id]/page.tsx b/src/app/dev/profile/edit/[id]/page.tsx index 53e6ca6d..1941dc84 100644 --- a/src/app/dev/profile/edit/[id]/page.tsx +++ b/src/app/dev/profile/edit/[id]/page.tsx @@ -1,14 +1,12 @@ - import EditProfile from "@/app_modules/katalog/profile/edit/view"; import { Profile_getOneProfileAndUserById } from "@/app_modules/katalog/profile/fun/get/get_one_user_profile"; export default async function Page({ params }: { params: { id: string } }) { - let profileId = params.id - const dataProfile = await Profile_getOneProfileAndUserById(profileId) + let profileId = params.id; + const dataProfile = await Profile_getOneProfileAndUserById(profileId); return ( <> - {/* {JSON.stringify(data)} */} ); 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 642feddc..8cd0b16b 100644 --- a/src/app_modules/_global/button/comp_button_upload_photo.tsx +++ b/src/app_modules/_global/button/comp_button_upload_photo.tsx @@ -3,6 +3,8 @@ import { Button, FileButton } from "@mantine/core"; import { IconCamera } from "@tabler/icons-react"; import { MainColor } from "../color"; +import { MAX_SIZE } from "../lib"; +import { ComponentGlobal_NotifikasiPeringatan } from "../notif_global"; export function ComponentGlobal_ButtonUploadFileImage({ onSetFile, @@ -19,8 +21,14 @@ export function ComponentGlobal_ButtonUploadFileImage({ new Blob([new Uint8Array(await files.arrayBuffer())]) ); - onSetFile(files); - onSetImage(buffer); + if (files.size > MAX_SIZE) { + ComponentGlobal_NotifikasiPeringatan( + "Ukuran file terlalu besar. Maksimal 2 MB." + ); + } else { + onSetFile(files); + onSetImage(buffer); + } } catch (error) { console.log(error); } diff --git a/src/app_modules/_global/lib/index.ts b/src/app_modules/_global/lib/index.ts new file mode 100644 index 00000000..78b13e14 --- /dev/null +++ b/src/app_modules/_global/lib/index.ts @@ -0,0 +1,3 @@ +import { MAX_SIZE } from "./max_size"; + +export { MAX_SIZE }; diff --git a/src/app_modules/_global/lib/max_size.ts b/src/app_modules/_global/lib/max_size.ts new file mode 100644 index 00000000..d312cf45 --- /dev/null +++ b/src/app_modules/_global/lib/max_size.ts @@ -0,0 +1,2 @@ +// Maksimal ukuran file dalam byte (2 MB) +export const MAX_SIZE = 2 * 1024 * 1024; // 2 MB diff --git a/src/app_modules/_global/ui/ui_image_preview.tsx b/src/app_modules/_global/ui/ui_image_preview.tsx index 9f37411a..a2057b2d 100644 --- a/src/app_modules/_global/ui/ui_image_preview.tsx +++ b/src/app_modules/_global/ui/ui_image_preview.tsx @@ -28,7 +28,7 @@ export function UIGlobal_ImagePreview({ fileId }: { fileId: string }) { const [isImage, setIsImage] = useState(null); const [isLoading, setIsLoading] = useState(false); - const url = APIs.GET({ fileId: fileId }); + const url = APIs.GET({ fileId: fileId, size: "500" }); useShallowEffect(() => { onLoadImage(); diff --git a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx index e523a2ba..d649d346 100644 --- a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx +++ b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx @@ -9,15 +9,13 @@ import { ComponentGlobal_NotifikasiGagal, ComponentGlobal_NotifikasiPeringatan, } from "@/app_modules/_global/notif_global"; +import { gmailRegex } from "@/app_modules/katalog/component/regular_expressions"; import { Button } from "@mantine/core"; import _ from "lodash"; import { useRouter } from "next/navigation"; import { useState } from "react"; import funCreateNewProfile from "../../fun/fun_create_profile"; import { MODEL_PROFILE } from "../../model/interface"; -import { validRegex } from "@/app_modules/katalog/component"; -import { envs } from "@/lib/envs"; -import { TokenProvider, TokenStorage } from "@/app/lib/token"; export function Profile_ComponentCreateNewProfile({ value, @@ -40,7 +38,8 @@ export function Profile_ComponentCreateNewProfile({ }; if (_.values(newData).includes("")) return ComponentGlobal_NotifikasiPeringatan("Lengkapi Data"); - if (!newData.email.match(validRegex)) return null; + if (!newData.email.match(gmailRegex)) + return ComponentGlobal_NotifikasiPeringatan("Format email salah"); if (filePP == null) return ComponentGlobal_NotifikasiPeringatan("Lengkapi foto profile"); diff --git a/src/app_modules/katalog/profile/create/view.tsx b/src/app_modules/katalog/profile/create/view.tsx index c97942ee..dce45a60 100644 --- a/src/app_modules/katalog/profile/create/view.tsx +++ b/src/app_modules/katalog/profile/create/view.tsx @@ -6,6 +6,8 @@ import { ComponentGlobal_BoxUploadImage, ComponentGlobal_ErrorInput, } from "@/app_modules/_global/component"; +import { MAX_SIZE } from "@/app_modules/_global/lib"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; import { AspectRatio, Avatar, @@ -22,10 +24,8 @@ import { } from "@mantine/core"; import { IconAt, IconCamera, IconUpload } from "@tabler/icons-react"; import { useState } from "react"; -import { validRegex } from "../../component"; -import { Profile_ComponentCreateNewProfile } from "../_component"; -import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; import { gmailRegex } from "../../component/regular_expressions"; +import { Profile_ComponentCreateNewProfile } from "../_component"; export default function CreateProfile() { const [filePP, setFilePP] = useState(null); @@ -40,9 +40,6 @@ export default function CreateProfile() { jenisKelamin: "", }); - // Maksimal ukuran file dalam byte (2 MB) - const MAX_SIZE = 2 * 1024 * 1024; // 2 MB - return ( <> diff --git a/src/app_modules/katalog/profile/edit/view.tsx b/src/app_modules/katalog/profile/edit/view.tsx index 8a907706..9b33f424 100644 --- a/src/app_modules/katalog/profile/edit/view.tsx +++ b/src/app_modules/katalog/profile/edit/view.tsx @@ -4,14 +4,14 @@ import { Button, Loader, Select, Stack, TextInput } from "@mantine/core"; import _ from "lodash"; import { useRouter } from "next/navigation"; import { useState } from "react"; - import { MainColor } from "@/app_modules/_global/color/color_pallet"; import ComponentGlobal_ErrorInput from "@/app_modules/_global/component/error_input"; import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; -import { validRegex } from "../../component/regular_expressions"; +import { gmailRegex, validRegex } from "../../component/regular_expressions"; import { Profile_funEditById } from "../fun/update/fun_edit_profile_by_id"; import { MODEL_PROFILE } from "../model/interface"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; export default function EditProfile({ data }: { data: MODEL_PROFILE }) { const router = useRouter(); @@ -24,8 +24,10 @@ export default function EditProfile({ data }: { data: MODEL_PROFILE }) { const body = dataProfile; // console.log(body) - if (_.values(body).includes("")) return null; - if (!body.email.match(validRegex)) return null; + if (_.values(body).includes("")) + return ComponentGlobal_NotifikasiPeringatan("Lengkapi data"); + if (!body.email.match(gmailRegex)) + return ComponentGlobal_NotifikasiPeringatan("Format email salah"); await Profile_funEditById(body).then((res) => { if (res.status === 200) { @@ -126,7 +128,7 @@ export default function EditProfile({ data }: { data: MODEL_PROFILE }) { dataProfile?.email === "" ? ( ) : dataProfile?.email?.length > 0 && - !dataProfile?.email.match(validRegex) ? ( + !dataProfile?.email.match(gmailRegex) ? ( ) : ( "" diff --git a/src/app_modules/katalog/profile/upload/foto_background/index.tsx b/src/app_modules/katalog/profile/upload/foto_background/index.tsx index 12d03b46..559b003c 100644 --- a/src/app_modules/katalog/profile/upload/foto_background/index.tsx +++ b/src/app_modules/katalog/profile/upload/foto_background/index.tsx @@ -30,7 +30,10 @@ export default function Profile_UpdateFotoBackground({ src={ image ? image - : APIs.GET({ fileId: profile.imageBackgroundId as any }) + : APIs.GET({ + fileId: profile.imageBackgroundId as any, + size: "400", + }) } /> diff --git a/src/app_modules/katalog/profile/upload/foto_profile/index.tsx b/src/app_modules/katalog/profile/upload/foto_profile/index.tsx index 6e49f09f..4f1a960e 100644 --- a/src/app_modules/katalog/profile/upload/foto_profile/index.tsx +++ b/src/app_modules/katalog/profile/upload/foto_profile/index.tsx @@ -27,7 +27,11 @@ export default function UploadFotoProfile({ Avatar From 8823a01b067928cbeea195402f2bb454df8e8bea Mon Sep 17 00:00:00 2001 From: amel Date: Wed, 11 Dec 2024 14:02:04 +0800 Subject: [PATCH 046/595] fix: loading button tambah portofolio Deskripsi: - loading button selanjutjnya - loading button simpan pin No Issues --- .../component/button/comp_button_selanjutnya.tsx | 2 +- .../map/_component/button/comp_button_save_pin.tsx | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx b/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx index c7e3fcd5..41c037b9 100644 --- a/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx +++ b/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx @@ -30,6 +30,7 @@ export function Portofolio_ComponentButtonSelanjutnya({ const [loading, setLoading] = useState(false); async function onSubmit() { + setLoading(true); const porto = { namaBisnis: dataPortofolio.namaBisnis, masterBidangBisnisId: dataPortofolio.masterBidangBisnisId, @@ -56,7 +57,6 @@ export function Portofolio_ComponentButtonSelanjutnya({ fileId: uploadFileToStorage.data.id, }); if (res.status === 201) { - setLoading(true); ComponentGlobal_NotifikasiBerhasil("Berhasil disimpan"); router.replace(RouterMap.create + res.id, { scroll: false }); } else { diff --git a/src/app_modules/map/_component/button/comp_button_save_pin.tsx b/src/app_modules/map/_component/button/comp_button_save_pin.tsx index 8af85a08..40a398d2 100644 --- a/src/app_modules/map/_component/button/comp_button_save_pin.tsx +++ b/src/app_modules/map/_component/button/comp_button_save_pin.tsx @@ -11,6 +11,7 @@ import { useRouter } from "next/navigation"; import { map_funCreatePin } from "../../fun/create/fun_create_pin"; import { DIRECTORY_ID } from "@/app/lib"; import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; +import { useState } from "react"; export function ComponentMap_ButtonSavePin({ namePin, @@ -26,7 +27,10 @@ export function ComponentMap_ButtonSavePin({ file: File; }) { const router = useRouter(); + const [loading, setLoading] = useState(false) + async function onSavePin() { + setLoading(true) const uploadFileToStorage = await funGlobal_UploadToStorage({ file: file, dirId: DIRECTORY_ID.map_image, @@ -49,15 +53,19 @@ export function ComponentMap_ButtonSavePin({ res.status === 200 ? (ComponentGlobal_NotifikasiBerhasil(res.message), router.back()) : ComponentGlobal_NotifikasiGagal(res.message); + + setLoading(false) } return ( <> + + + )} + + + { + loading + ? + : + _.isEmpty(data) ? ( + + ) : ( + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllDonasi(`?cat=beranda&page=${pageNew}`) + + setActivePage((val) => val + 1); + + return loadData; + }} + > + {(item) => ( + + )} +
+ ) + } + + + ); +} diff --git a/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx b/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx new file mode 100644 index 00000000..dea0613a --- /dev/null +++ b/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx @@ -0,0 +1,69 @@ +"use client"; +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { AccentColor, MainColor, } from "@/app_modules/_global/color/color_pallet"; +import { MODEL_NEW_DEFAULT_MASTER } from "@/app_modules/model_global/interface"; +import { Stack, Tabs } from "@mantine/core"; +import { useParams, useRouter } from "next/navigation"; + +export default function GalangDanaDonasiNew({listStatus,}: {listStatus: MODEL_NEW_DEFAULT_MASTER[]; }) { + const router = useRouter(); + const param = useParams<{ id: string }>(); + + async function onChangeStatus({ statusId }: { statusId: string }) { + router.replace(RouterDonasi.status_galang_dana({ id: statusId })); + } + + return ( + <> + { + onChangeStatus({ statusId: val }); + }} + styles={{ + tabsList: { + backgroundColor: MainColor.darkblue, + position: "sticky", + top: 0, + zIndex: 99, + }, + }} + > + + + {listStatus.map((e, i) => ( + + {e.name} + + ))} + + + {/* {param.id == "1" && } + + {param.id == "2" && } + + {param.id == "3" && } + + {param.id == "4" && } */} + + + + ); +} From 80b6d35fe949cd04b04e258f58961254f1e32c64 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 12 Dec 2024 14:44:51 +0800 Subject: [PATCH 054/595] chore(release): 1.2.25 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f6be3ff..309eb639 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.25](https://github.com/bipproduction/hipmi/compare/v1.2.24...v1.2.25) (2024-12-12) + ## [1.2.24](https://github.com/bipproduction/hipmi/compare/v1.2.23...v1.2.24) (2024-12-11) diff --git a/package.json b/package.json index 06b12f78..73e48807 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.24", + "version": "1.2.25", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From d1f79661232f1816026f9d2d64dbcb495b5a11f2 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 12 Dec 2024 14:46:06 +0800 Subject: [PATCH 055/595] Fix Job Deskripsi: - Di beranda diganti menggunakan API - Skeleton beranda - V 1.2.25 --- src/app/api/job/get-all/route.ts | 16 ++ src/app/dev/job/main/beranda/page.tsx | 5 +- src/app/lib/api_user_router/route_api_job.ts | 4 + src/app/lib/prisma.ts | 2 + .../fun/upload/fun_upload_to_storage.ts | 26 ++- .../donasi/fun/get/fun_check_status.tsx | 1 - .../button/comp_button_save_create.tsx | 165 +++++++++--------- src/app_modules/job/component/index.ts | 2 + .../skeleton/comp_skeleton_beranda.tsx | 25 +++ src/app_modules/job/detail/main/view.tsx | 17 +- .../job/main/beranda/ui_beranda.tsx | 48 ++--- .../job/main/beranda/view_beranda.tsx | 4 +- .../button/comp_create_new_profile.tsx | 29 +-- .../katalog/profile/fun/fun_create_profile.ts | 76 ++++---- src/middleware.ts | 1 + 15 files changed, 253 insertions(+), 168 deletions(-) create mode 100644 src/app/api/job/get-all/route.ts create mode 100644 src/app/lib/api_user_router/route_api_job.ts create mode 100644 src/app_modules/job/component/skeleton/comp_skeleton_beranda.tsx diff --git a/src/app/api/job/get-all/route.ts b/src/app/api/job/get-all/route.ts new file mode 100644 index 00000000..21db076c --- /dev/null +++ b/src/app/api/job/get-all/route.ts @@ -0,0 +1,16 @@ +import { job_getAllListPublish } from "@/app_modules/job/fun/get/get_all_publish"; +import _ from "lodash"; +import { NextResponse } from "next/server"; + +export async function GET(params: Request) { + const { searchParams } = new URL(params.url); + const page = searchParams.get("page"); + const search = searchParams.get("search"); + + const data = await job_getAllListPublish({ + page: _.toNumber(page), + search: search as string, + }); + + return NextResponse.json({ data }); +} diff --git a/src/app/dev/job/main/beranda/page.tsx b/src/app/dev/job/main/beranda/page.tsx index 1a4bce82..c8cf14d0 100644 --- a/src/app/dev/job/main/beranda/page.tsx +++ b/src/app/dev/job/main/beranda/page.tsx @@ -1,12 +1,9 @@ import { Job_ViewBeranda } from "@/app_modules/job"; -import { job_getAllListPublish } from "@/app_modules/job/fun/get/get_all_publish"; export default async function Page() { - const listJob = await job_getAllListPublish({ page: 1 }); - return ( <> - + ); } diff --git a/src/app/lib/api_user_router/route_api_job.ts b/src/app/lib/api_user_router/route_api_job.ts new file mode 100644 index 00000000..66234665 --- /dev/null +++ b/src/app/lib/api_user_router/route_api_job.ts @@ -0,0 +1,4 @@ +export const API_RouteJob = { + get_all: ({ page, search }: { page: number; search?: string }) => + `/api/job/get-all?page=${page}&search=${search || ""}`, +}; diff --git a/src/app/lib/prisma.ts b/src/app/lib/prisma.ts index a0d1a749..25a08ef9 100644 --- a/src/app/lib/prisma.ts +++ b/src/app/lib/prisma.ts @@ -23,4 +23,6 @@ process.on('SIGINT', async () => { process.exit(0); }); +// console.log('==> Test prisma'); + export default prisma; 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 52155b9b..06ed38f4 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 @@ -1,6 +1,4 @@ -import { ServerEnv } from "@/app/lib/server_env"; import { TokenStorage } from "@/app/lib/token"; -import { envs } from "@/lib/envs"; export async function funGlobal_UploadToStorage({ file, @@ -24,9 +22,19 @@ export async function funGlobal_UploadToStorage({ "text/plain", ]; - if (!allowedMimeTypes.includes(file.type)) console.log("File tidak sesuai"); + // if (!allowedMimeTypes.includes(file.type)) console.log("File tidak sesuai"); + if (!allowedMimeTypes.includes(file.type)) { + console.error("File tidak sesuai"); + return { success: false, message: "File type not allowed" }; + } - if (file.size > 100 * 1024 * 1024) console.log("File terlalu besar"); + if (file.size > 100 * 1024 * 1024) { + 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); @@ -39,19 +47,23 @@ export async function funGlobal_UploadToStorage({ headers: { Authorization: `Bearer ${Env_WS_APIKEY}`, }, + signal: controller.signal, }); - const dataRes = await res.json(); + clearTimeout(timeoutId); // Bersihkan timeout jika selesai tepat waktu 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, data: {} }; + return { success: false, message: errorText }; } } catch (error) { + clearTimeout(timeoutId); // + console.error("Error:", error); - return { success: false, data: {} }; + return { success: false, message: "An unexpected error occurred" }; } } diff --git a/src/app_modules/donasi/fun/get/fun_check_status.tsx b/src/app_modules/donasi/fun/get/fun_check_status.tsx index 1b751153..2ff3fad7 100644 --- a/src/app_modules/donasi/fun/get/fun_check_status.tsx +++ b/src/app_modules/donasi/fun/get/fun_check_status.tsx @@ -9,7 +9,6 @@ export async function donasi_checkStatus({ id }: { id: string }) { }, }); - console.log(checkStatus?.donasiMaster_StatusDonasiId, "ini status nya") if (checkStatus?.donasiMaster_StatusDonasiId == "2") return true; return false; diff --git a/src/app_modules/job/component/button/comp_button_save_create.tsx b/src/app_modules/job/component/button/comp_button_save_create.tsx index 00a5e2e2..ccbe263c 100644 --- a/src/app_modules/job/component/button/comp_button_save_create.tsx +++ b/src/app_modules/job/component/button/comp_button_save_create.tsx @@ -33,92 +33,101 @@ function Job_ComponentButtonSaveCreate({ const [hotMenu, setHotMenu] = useAtom(gs_job_hot_menu); async function onCreate() { - if (file === null) { - const createNoFile = await job_funCreateNoFile({ - data: value, - }); - - if (createNoFile.status === 201) { - const dataNotifikasi: IRealtimeData = { - appId: createNoFile.data?.id as any, - status: createNoFile.data?.MasterStatus?.name as any, - userId: createNoFile.data?.authorId as any, - pesan: createNoFile.data?.title as any, - kategoriApp: "JOB", - title: "Job baru", - }; - - const notif = await notifikasiToAdmin_funCreate({ - data: dataNotifikasi as any, + try { + setIsLoading(true); + if (file === null) { + const createNoFile = await job_funCreateNoFile({ + data: value, }); - if (notif.status === 201) { - WibuRealtime.setData({ - type: "notification", - pushNotificationTo: "ADMIN", + if (createNoFile.status === 201) { + const dataNotifikasi: IRealtimeData = { + appId: createNoFile.data?.id as any, + status: createNoFile.data?.MasterStatus?.name as any, + userId: createNoFile.data?.authorId as any, + pesan: createNoFile.data?.title as any, + kategoriApp: "JOB", + title: "Job baru", + }; + + const notif = await notifikasiToAdmin_funCreate({ + data: dataNotifikasi as any, }); - WibuRealtime.setData({ - type: "trigger", - pushNotificationTo: "ADMIN", - dataMessage: dataNotifikasi, - }); + if (notif.status === 201) { + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "ADMIN", + }); - setHotMenu(2); - router.replace(RouterJob.status({ id: "2" })); - setIsLoading(true); - ComponentGlobal_NotifikasiBerhasil(createNoFile.message); + WibuRealtime.setData({ + type: "trigger", + pushNotificationTo: "ADMIN", + dataMessage: dataNotifikasi, + }); + + setHotMenu(2); + router.replace(RouterJob.status({ id: "2" })); + ComponentGlobal_NotifikasiBerhasil(createNoFile.message); + } + } else { + ComponentGlobal_NotifikasiGagal(createNoFile.message); } } else { - ComponentGlobal_NotifikasiGagal(createNoFile.message); + const uploadFile = await funGlobal_UploadToStorage({ + file: file, + dirId: DIRECTORY_ID.job_image, + }); + + if (!uploadFile.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal upload gambar"); + return; + } + + const createWithFile = await job_funCreateWithFile({ + data: value, + fileId: uploadFile.data.id, + }); + + if (createWithFile.status === 201) { + const dataNotifikasi: IRealtimeData = { + appId: createWithFile.data?.id as any, + status: createWithFile.data?.MasterStatus?.name as any, + userId: createWithFile.data?.authorId as any, + pesan: createWithFile.data?.title as any, + kategoriApp: "JOB", + title: "Job baru", + }; + + const notif = await notifikasiToAdmin_funCreate({ + data: dataNotifikasi as any, + }); + + if (notif.status === 201) { + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "ADMIN", + }); + + WibuRealtime.setData({ + type: "trigger", + pushNotificationTo: "ADMIN", + dataMessage: dataNotifikasi, + }); + + setHotMenu(2); + router.replace(RouterJob.status({ id: "2" })); + ComponentGlobal_NotifikasiBerhasil(createWithFile.message); + } + } else { + ComponentGlobal_NotifikasiGagal(createWithFile.message); + } } - } else { - const uploadFile = await funGlobal_UploadToStorage({ - file: file, - dirId: DIRECTORY_ID.job_image, - }); - - if (!uploadFile.success) - return ComponentGlobal_NotifikasiPeringatan("Gagal upload gambar"); - - const createWithFile = await job_funCreateWithFile({ - data: value, - fileId: uploadFile.data.id, - }); - - if (createWithFile.status === 201) { - const dataNotifikasi: IRealtimeData = { - appId: createWithFile.data?.id as any, - status: createWithFile.data?.MasterStatus?.name as any, - userId: createWithFile.data?.authorId as any, - pesan: createWithFile.data?.title as any, - kategoriApp: "JOB", - title: "Job baru", - }; - - const notif = await notifikasiToAdmin_funCreate({ - data: dataNotifikasi as any, - }); - - if (notif.status === 201) { - WibuRealtime.setData({ - type: "notification", - pushNotificationTo: "ADMIN", - }); - - WibuRealtime.setData({ - type: "trigger", - pushNotificationTo: "ADMIN", - dataMessage: dataNotifikasi, - }); - - setHotMenu(2); - router.replace(RouterJob.status({ id: "2" })); - setIsLoading(true); - ComponentGlobal_NotifikasiBerhasil(createWithFile.message); - } - } else { - ComponentGlobal_NotifikasiGagal(createWithFile.message); + } catch (error) { + console.log(error); + } finally { + if (window.location.pathname !== RouterJob.status({ id: "2" })) { + setIsLoading(false); } } } diff --git a/src/app_modules/job/component/index.ts b/src/app_modules/job/component/index.ts index 810f60b3..156a94cd 100644 --- a/src/app_modules/job/component/index.ts +++ b/src/app_modules/job/component/index.ts @@ -2,8 +2,10 @@ import Job_ComponentButtonSaveCreate from "./button/comp_button_save_create"; import { Job_ComponentButtonUpdateBeranda } from "./button/comp_button_update_beranda"; import { Job_ComponentButtonUpdateData } from "./button/comp_button_update_data"; import { Job_ComponentBoxUploadImage } from "./detail/comp_box_upload_image"; +import Job_ComponentSkeletonBeranda from "./skeleton/comp_skeleton_beranda"; export { Job_ComponentButtonSaveCreate }; export { Job_ComponentBoxUploadImage }; export { Job_ComponentButtonUpdateData as Job_ComponentButtonUpdate }; export { Job_ComponentButtonUpdateBeranda }; +export { Job_ComponentSkeletonBeranda }; diff --git a/src/app_modules/job/component/skeleton/comp_skeleton_beranda.tsx b/src/app_modules/job/component/skeleton/comp_skeleton_beranda.tsx new file mode 100644 index 00000000..7e773ed6 --- /dev/null +++ b/src/app_modules/job/component/skeleton/comp_skeleton_beranda.tsx @@ -0,0 +1,25 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Box, Center, Group, Skeleton, Stack } from "@mantine/core"; + +export default function Job_ComponentSkeletonBeranda() { + return ( + <> + + {Array.from(new Array(10)).map((e, i) => ( + + + + + + + +
+ +
+
+
+ ))} +
+ + ); +} diff --git a/src/app_modules/job/detail/main/view.tsx b/src/app_modules/job/detail/main/view.tsx index a9e21aec..e2bcdf6d 100644 --- a/src/app_modules/job/detail/main/view.tsx +++ b/src/app_modules/job/detail/main/view.tsx @@ -24,19 +24,10 @@ function ButtonAction({ jobId }: { jobId: string }) { const [origin, setOrigin] = useState(""); useShallowEffect(() => { - onLoadOrigin(setOrigin); - // if (typeof window !== "undefined") { - // setOrigin(window.location.origin); - // } - - }, [setOrigin]); - - async function onLoadOrigin(setOrigin: any) { - const res = await fetch("/api/origin-url"); - const result = await res.json(); - console.log(result); - setOrigin(result.origin); - } + if (typeof window !== "undefined") { + setOrigin(window.location.origin); + } + }, []); return ( <> diff --git a/src/app_modules/job/main/beranda/ui_beranda.tsx b/src/app_modules/job/main/beranda/ui_beranda.tsx index 6b0a8505..0783e9f7 100644 --- a/src/app_modules/job/main/beranda/ui_beranda.tsx +++ b/src/app_modules/job/main/beranda/ui_beranda.tsx @@ -4,20 +4,24 @@ import { gs_jobTiggerBeranda } from "@/app/lib/global_state"; import { RouterJob } from "@/app/lib/router_hipmi/router_job"; import ComponentGlobal_CreateButton from "@/app_modules/_global/component/button_create"; import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; -import { Center, Loader, Stack, TextInput } from "@mantine/core"; +import { Box, Center, Loader, Stack, TextInput } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { IconSearch } from "@tabler/icons-react"; import { useAtom } from "jotai"; import _ from "lodash"; import { ScrollOnly } from "next-scroll-loader"; -import { useEffect, useState } from "react"; +import { useState } from "react"; +import { + Job_ComponentButtonUpdateBeranda, + Job_ComponentSkeletonBeranda, +} from "../../component"; import ComponentJob_BerandaCardView from "../../component/beranda/card_view"; import { job_getAllListPublish } from "../../fun/get/get_all_publish"; import { MODEL_JOB } from "../../model/interface"; -import { Job_ComponentButtonUpdateBeranda } from "../../component"; +import { API_RouteJob } from "@/app/lib/api_user_router/route_api_job"; -export function Job_UiBeranda({ listData }: { listData: MODEL_JOB[] }) { - const [data, setData] = useState(listData); +export function Job_UiBeranda() { + const [data, setData] = useState(null); const [activePage, setActivePage] = useState(1); const [isSearch, setIsSearch] = useState(""); @@ -26,19 +30,16 @@ export function Job_UiBeranda({ listData }: { listData: MODEL_JOB[] }) { const [isTriggerJob, setIsTriggerJob] = useAtom(gs_jobTiggerBeranda); useShallowEffect(() => { - onLoadNewData({ - onLoad(val) { - setData(val); - }, - }); - }, [setData]); + if (isTriggerJob == true) { + setIsShowUpdate(true); + } + }, [isTriggerJob]); useShallowEffect(() => { - if (isTriggerJob) { - setIsShowUpdate(true); - // setIsTriggerJob(false); - } - }, [isTriggerJob, setIsShowUpdate]); + setIsTriggerJob(false); + setIsShowUpdate(false); + onLoadNewData(); + }, []); async function onSearch(text: string) { setIsSearch(text); @@ -50,9 +51,11 @@ export function Job_UiBeranda({ listData }: { listData: MODEL_JOB[] }) { setActivePage(1); } - async function onLoadNewData({ onLoad }: { onLoad: (val: any) => void }) { - const loadData = await job_getAllListPublish({ page: 1 }); - onLoad(loadData); + async function onLoadNewData() { + const loadData = await fetch(API_RouteJob.get_all({ page: activePage })); + const res = await loadData.json(); + + setData(res.data); } return ( @@ -62,6 +65,7 @@ export function Job_UiBeranda({ listData }: { listData: MODEL_JOB[] }) { { setIsShowUpdate(val); + setIsTriggerJob(val); }} onSetData={(val: any[]) => { setData(val); @@ -85,7 +89,9 @@ export function Job_UiBeranda({ listData }: { listData: MODEL_JOB[] }) { }} /> - {_.isEmpty(data) ? ( + {_.isNull(data) ? ( + + ) : _.isEmpty(data) ? ( ) : ( // --- Main component --- // @@ -97,7 +103,7 @@ export function Job_UiBeranda({ listData }: { listData: MODEL_JOB[] }) { )} data={data} - setData={setData} + setData={setData as any} moreData={async () => { const loadData = await job_getAllListPublish({ page: activePage + 1, diff --git a/src/app_modules/job/main/beranda/view_beranda.tsx b/src/app_modules/job/main/beranda/view_beranda.tsx index c56f1feb..d6004905 100644 --- a/src/app_modules/job/main/beranda/view_beranda.tsx +++ b/src/app_modules/job/main/beranda/view_beranda.tsx @@ -1,10 +1,10 @@ import { MODEL_JOB } from "../../model/interface"; import { Job_UiBeranda } from "./ui_beranda"; -export default function Job_ViewBeranda({ listJob }: { listJob: MODEL_JOB[] }) { +export default function Job_ViewBeranda() { return ( <> - + ); } diff --git a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx index d649d346..104afedf 100644 --- a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx +++ b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx @@ -56,10 +56,8 @@ export function Profile_ComponentCreateNewProfile({ dirId: DIRECTORY_ID.profile_foto, }); if (!uploadPhoto.success) { - setLoading(false); - return ComponentGlobal_NotifikasiPeringatan( - "Gagal upload foto profile" - ); + ComponentGlobal_NotifikasiPeringatan("Gagal upload foto profile"); + return; } const uploadBackground = await funGlobal_UploadToStorage({ @@ -67,10 +65,8 @@ export function Profile_ComponentCreateNewProfile({ dirId: DIRECTORY_ID.profile_background, }); if (!uploadBackground.success) { - setLoading(false); - return ComponentGlobal_NotifikasiPeringatan( - "Gagal upload background profile" - ); + ComponentGlobal_NotifikasiPeringatan("Gagal upload background profile"); + return; } const create = await funCreateNewProfile({ @@ -78,15 +74,24 @@ export function Profile_ComponentCreateNewProfile({ imageId: uploadPhoto.data.id, imageBackgroundId: uploadBackground.data.id, }); + if (create.status === 201) { ComponentGlobal_NotifikasiBerhasil("Berhasil membuat profile", 3000); router.push(RouterHome.main_home, { scroll: false }); - } else { - ComponentGlobal_NotifikasiGagal(create.message); - setLoading(false); } + + if (create.status === 400) { + ComponentGlobal_NotifikasiGagal(create.message); + } + + if (create.status === 500) { + ComponentGlobal_NotifikasiGagal(create.message); + } + } catch (error) { - console.log(error); + console.log("Terjadi kesalahan", error); + } finally { + setLoading(false); } } diff --git a/src/app_modules/katalog/profile/fun/fun_create_profile.ts b/src/app_modules/katalog/profile/fun/fun_create_profile.ts index eb49f756..ff8eb96d 100644 --- a/src/app_modules/katalog/profile/fun/fun_create_profile.ts +++ b/src/app_modules/katalog/profile/fun/fun_create_profile.ts @@ -1,14 +1,10 @@ "use server"; import prisma from "@/app/lib/prisma"; -import { MODEL_PROFILE } from "../model/interface"; -import _ from "lodash"; -import { v4 } from "uuid"; -import fs from "fs"; -import { revalidatePath } from "next/cache"; import { RouterHome } from "@/app/lib/router_hipmi/router_home"; -import { Prisma } from "@prisma/client"; import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import { Prisma } from "@prisma/client"; +import { revalidatePath } from "next/cache"; export default async function funCreateNewProfile({ data, @@ -19,33 +15,53 @@ export default async function funCreateNewProfile({ imageId: string; imageBackgroundId: string; }) { - const userLoginId = await funGetUserIdByToken(); + try { + const userLoginId = await funGetUserIdByToken(); - const findEmail = await prisma.profile.findUnique({ - where: { - email: data.email, - }, - }); + if (!userLoginId) { + return { status: 400, message: "User tidak terautentikasi" }; // Validasi user login + } - if (findEmail) return { status: 400, message: "Email telah digunakan" }; + const findEmail = await prisma.profile.findUnique({ + where: { + email: data.email, + }, + }); - const createProfile = await prisma.profile.create({ - data: { - userId: userLoginId, - name: data.name, - email: data.email, - alamat: data.alamat, - jenisKelamin: data.jenisKelamin, - imageId: imageId, - imageBackgroundId: imageBackgroundId, - }, - }); + if (findEmail) { + return { status: 400, message: "Email telah digunakan" }; + } - if (!createProfile) return { status: 400, message: "Gagal membuat profile" }; - revalidatePath(RouterHome.main_home); + const createProfile = await prisma.profile.create({ + data: { + userId: userLoginId, + name: data.name, + email: data.email, + alamat: data.alamat, + jenisKelamin: data.jenisKelamin, + imageId: imageId, + imageBackgroundId: imageBackgroundId, + }, + }); - return { - status: 201, - message: "Berhasil", - }; + if (!createProfile) { + return { status: 400, message: "Gagal membuat profile" }; + } + + // Revalidate cache halaman home + try { + revalidatePath(RouterHome.main_home); + } catch (cacheError) { + console.error("Cache revalidation failed:", cacheError); + // Tidak membuat fungsi gagal, cukup log error cache + } + + return { + status: 201, + message: "Berhasil", + }; + } catch (error) { + console.error("Error creating profile:", error); + return { status: 500, message: "Terjadi kesalahan pada server" }; + } } diff --git a/src/middleware.ts b/src/middleware.ts index f4cd23ab..705192f2 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -20,6 +20,7 @@ const middlewareConfig: MiddlewareConfig = { publicRoutes: [ "/", "/api/upload", + "/api/job/*", "/api/validation", "/api/auth/*", "/api/origin-url", From 0bcb5182acc1fa4899761abab461945cd0e4a621 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 12 Dec 2024 15:08:19 +0800 Subject: [PATCH 056/595] chore(release): 1.2.26 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 309eb639..4cde977b 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.26](https://github.com/bipproduction/hipmi/compare/v1.2.25...v1.2.26) (2024-12-12) + ## [1.2.25](https://github.com/bipproduction/hipmi/compare/v1.2.24...v1.2.25) (2024-12-12) ## [1.2.24](https://github.com/bipproduction/hipmi/compare/v1.2.23...v1.2.24) (2024-12-11) diff --git a/package.json b/package.json index 73e48807..5813c14a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.25", + "version": "1.2.26", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 6ea9d3bbb1b0497efaf6272d122f5465cf4e33f7 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 12 Dec 2024 15:15:01 +0800 Subject: [PATCH 057/595] Fix version 1.2.26 --- src/app_modules/auth/register/view.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/app_modules/auth/register/view.tsx b/src/app_modules/auth/register/view.tsx index a8b6f985..79d4b59a 100644 --- a/src/app_modules/auth/register/view.tsx +++ b/src/app_modules/auth/register/view.tsx @@ -64,11 +64,6 @@ export default function Register() { const result = await res.json(); - if (res.status === 400) { - setLoading(false); - ComponentGlobal_NotifikasiPeringatan(result.message); - } - if (res.status === 200) { localStorage.removeItem("hipmi_auth_code_id"); ComponentGlobal_NotifikasiBerhasil(result.message); @@ -78,8 +73,15 @@ export default function Register() { nomor: data.nomor, }); } + + if (res.status === 400) { + ComponentGlobal_NotifikasiPeringatan(result.message); + return; + } } catch (error) { console.log(error); + } finally { + setLoading(false); } } From 180a10329ef07ec5aebdb8480653f13f951f238d Mon Sep 17 00:00:00 2001 From: amel Date: Thu, 12 Dec 2024 15:53:44 +0800 Subject: [PATCH 058/595] upd: donasi Deskripsi: - loading button tambah donasi - loading button pada halaman crowd-main - router path pada donasi-beranda NO Issues --- .../_global/component/button_create.tsx | 8 ++++++-- src/app_modules/crowd/main/view.tsx | 16 ++++++++++++---- .../component/card_view/card_publish_new.tsx | 14 ++++++-------- src/app_modules/donasi/main/beranda_new.tsx | 5 +---- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/app_modules/_global/component/button_create.tsx b/src/app_modules/_global/component/button_create.tsx index 892cc258..6fa820ab 100644 --- a/src/app_modules/_global/component/button_create.tsx +++ b/src/app_modules/_global/component/button_create.tsx @@ -40,11 +40,15 @@ export default function ComponentGlobal_CreateButton({ router.push(path); }} > - {isLoading ? ( + {/* PAKE LOADING */} + {/* {isLoading ? ( ) : ( - )} + )} */} + + {/* GA PAKE LOADING */} + ); diff --git a/src/app_modules/crowd/main/view.tsx b/src/app_modules/crowd/main/view.tsx index 026d2c06..b3d4787d 100644 --- a/src/app_modules/crowd/main/view.tsx +++ b/src/app_modules/crowd/main/view.tsx @@ -74,11 +74,15 @@ export default function MainCrowd() { - {loadingInv ? ( + {/* PAKE LOADING */} + {/* {loadingInv ? ( ) : ( - )} + )} */} + + {/* GA PAKE LOADING */} +
@@ -114,11 +118,15 @@ export default function MainCrowd() { - {loadingDon ? ( + {/* PAKE LOADING */} + {/* {loadingDon ? ( ) : ( - )} + )} */} + + {/* GA PAKE LOADINGF */} + diff --git a/src/app_modules/donasi/component/card_view/card_publish_new.tsx b/src/app_modules/donasi/component/card_view/card_publish_new.tsx index ae9f6cb6..ab84a3a2 100644 --- a/src/app_modules/donasi/component/card_view/card_publish_new.tsx +++ b/src/app_modules/donasi/component/card_view/card_publish_new.tsx @@ -1,23 +1,21 @@ "use client"; -import { ComponentGlobal_CardLoadingOverlay, ComponentGlobal_CardStyles, ComponentGlobal_LoadImageCustom, } from "@/app_modules/_global/component"; +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { ComponentGlobal_CardStyles, ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; import { Grid, Progress, Stack, Text } from "@mantine/core"; import { useRouter } from "next/navigation"; -import { useState } from "react"; import ComponentDonasi_TampilanHitungMundur from "../tampilan_hitung_mundur"; import TampilanRupiahDonasi from "../tampilan_rupiah"; -export default function ComponentDonasi_CardPublishNew({ data, path, }: { data: any; path: string; }) { +export default function ComponentDonasi_CardPublishNew({ data }: { data: any; }) { const router = useRouter(); - const [visible, setVisible] = useState(false); - const [donasiId, setDonasiId] = useState(""); return ( <> { - setVisible(true); - setDonasiId(data.id); - router.push(path + `${data.id}`); + // DELSOON + console.log(RouterDonasi.detail_main + `${data.id}`) + router.push(RouterDonasi.detail_main + `${data.id}`) }} > diff --git a/src/app_modules/donasi/main/beranda_new.tsx b/src/app_modules/donasi/main/beranda_new.tsx index 87f9a696..04385702 100644 --- a/src/app_modules/donasi/main/beranda_new.tsx +++ b/src/app_modules/donasi/main/beranda_new.tsx @@ -121,10 +121,7 @@ export default function MainDonasiNew() { }} > {(item) => ( - + )} ) From ebdd5c80148b2ddd58ce978c083f551d61a3c365 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 12 Dec 2024 16:10:02 +0800 Subject: [PATCH 059/595] chore(release): 1.2.27 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cde977b..30560fe0 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.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) ## [1.2.25](https://github.com/bipproduction/hipmi/compare/v1.2.24...v1.2.25) (2024-12-12) diff --git a/package.json b/package.json index 5813c14a..f18b9dad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.26", + "version": "1.2.27", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 4e41cf4e14377bab22b64705ec8ecc7b9fe7e663 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 12 Dec 2024 16:14:24 +0800 Subject: [PATCH 060/595] Fix bug profile --- .../button/comp_create_new_profile.tsx | 52 ++++++++++--------- .../katalog/profile/create/view.tsx | 44 +++++++++++++--- 2 files changed, 65 insertions(+), 31 deletions(-) diff --git a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx index 104afedf..a8207043 100644 --- a/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx +++ b/src/app_modules/katalog/profile/_component/button/comp_create_new_profile.tsx @@ -19,12 +19,16 @@ import { MODEL_PROFILE } from "../../model/interface"; export function Profile_ComponentCreateNewProfile({ value, - filePP, - fileBG, + // filePP, + // fileBG, + fotoProfileId, + backgroundProfileId, }: { value: MODEL_PROFILE; - filePP: File; - fileBG: File; + // filePP: File; + // fileBG: File; + fotoProfileId: string; + backgroundProfileId: string; }) { const router = useRouter(); const [loading, setLoading] = useState(false); @@ -41,9 +45,9 @@ export function Profile_ComponentCreateNewProfile({ if (!newData.email.match(gmailRegex)) return ComponentGlobal_NotifikasiPeringatan("Format email salah"); - if (filePP == null) + if (fotoProfileId == "") return ComponentGlobal_NotifikasiPeringatan("Lengkapi foto profile"); - if (fileBG == null) + if (backgroundProfileId == null) return ComponentGlobal_NotifikasiPeringatan( "Lengkapi background profile" ); @@ -51,28 +55,28 @@ export function Profile_ComponentCreateNewProfile({ try { setLoading(true); - const uploadPhoto = await funGlobal_UploadToStorage({ - file: filePP, - dirId: DIRECTORY_ID.profile_foto, - }); - if (!uploadPhoto.success) { - ComponentGlobal_NotifikasiPeringatan("Gagal upload foto profile"); - return; - } + // const uploadPhoto = await funGlobal_UploadToStorage({ + // file: filePP, + // dirId: DIRECTORY_ID.profile_foto, + // }); + // if (!uploadPhoto.success) { + // ComponentGlobal_NotifikasiPeringatan("Gagal upload foto profile"); + // return; + // } - const uploadBackground = await funGlobal_UploadToStorage({ - file: fileBG, - dirId: DIRECTORY_ID.profile_background, - }); - if (!uploadBackground.success) { - ComponentGlobal_NotifikasiPeringatan("Gagal upload background profile"); - return; - } + // const uploadBackground = await funGlobal_UploadToStorage({ + // file: fileBG, + // dirId: DIRECTORY_ID.profile_background, + // }); + // if (!uploadBackground.success) { + // ComponentGlobal_NotifikasiPeringatan("Gagal upload background profile"); + // return; + // } const create = await funCreateNewProfile({ data: newData as any, - imageId: uploadPhoto.data.id, - imageBackgroundId: uploadBackground.data.id, + imageId: fotoProfileId, + imageBackgroundId: backgroundProfileId, }); if (create.status === 201) { diff --git a/src/app_modules/katalog/profile/create/view.tsx b/src/app_modules/katalog/profile/create/view.tsx index 1c3a1578..77f2c180 100644 --- a/src/app_modules/katalog/profile/create/view.tsx +++ b/src/app_modules/katalog/profile/create/view.tsx @@ -1,12 +1,15 @@ "use client"; +import { DIRECTORY_ID } from "@/app/lib"; import { MainColor } from "@/app_modules/_global/color"; import { ComponentGlobal_BoxInformation, ComponentGlobal_BoxUploadImage, ComponentGlobal_ErrorInput, } from "@/app_modules/_global/component"; +import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; import { MAX_SIZE } from "@/app_modules/_global/lib"; +import { PemberitahuanMaksimalFile } from "@/app_modules/_global/lib/max_size"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; import { AspectRatio, @@ -26,13 +29,14 @@ import { IconAt, IconCamera, IconUpload } from "@tabler/icons-react"; import { useState } from "react"; import { gmailRegex } from "../../component/regular_expressions"; import { Profile_ComponentCreateNewProfile } from "../_component"; -import { PemberitahuanMaksimalFile } from "@/app_modules/_global/lib/max_size"; export default function CreateProfile() { const [filePP, setFilePP] = useState(null); const [imgPP, setImgPP] = useState(); const [fileBG, setFileBG] = useState(null); const [imgBG, setImgBG] = useState(); + const [fotoProfileId, setFotoProfileId] = useState(""); + const [backgroundProfileId, setBackgroundProfileId] = useState(""); const [value, setValue] = useState({ name: "", @@ -92,8 +96,20 @@ export default function CreateProfile() { PemberitahuanMaksimalFile ); } else { - setImgPP(buffer); - setFilePP(files); + const uploadPhoto = await funGlobal_UploadToStorage({ + file: files, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (uploadPhoto.success) { + setFotoProfileId(uploadPhoto.data.id); + setImgPP(buffer); + setFilePP(files); + } else { + ComponentGlobal_NotifikasiPeringatan( + "Gagal upload foto profile" + ); + } } } catch (error) { console.log(error); @@ -154,8 +170,20 @@ export default function CreateProfile() { PemberitahuanMaksimalFile ); } else { - setImgBG(buffer); - setFileBG(files); + const uploadBackground = await funGlobal_UploadToStorage({ + file: files, + dirId: DIRECTORY_ID.profile_background, + }); + + if (uploadBackground.success) { + setBackgroundProfileId(uploadBackground.data.id); + setImgBG(buffer); + setFileBG(files); + } else { + ComponentGlobal_NotifikasiPeringatan( + "Gagal upload background profile" + ); + } } } catch (error) { console.log(error); @@ -256,8 +284,10 @@ export default function CreateProfile() { From bd2016deb3310d77ea52e7c9635724038ef38291 Mon Sep 17 00:00:00 2001 From: amel Date: Thu, 12 Dec 2024 17:21:31 +0800 Subject: [PATCH 061/595] upd: donasi Deskripsi: - update api master untuk create donasi No Issues --- src/app/api/new/donasi/master/route.ts | 33 +++ .../dev/donasi/create/create_donasi/page.tsx | 14 +- .../create/create_cerita_penggalang.tsx | 2 +- .../donasi/create/create_donasi_new.tsx | 264 ++++++++++++++++++ src/app_modules/donasi/index.ts | 2 + src/app_modules/donasi/lib/api_donasi.ts | 5 + 6 files changed, 311 insertions(+), 9 deletions(-) create mode 100644 src/app/api/new/donasi/master/route.ts create mode 100644 src/app_modules/donasi/create/create_donasi_new.tsx 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/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_modules/donasi/create/create_cerita_penggalang.tsx b/src/app_modules/donasi/create/create_cerita_penggalang.tsx index 046a4cef..ae5defbd 100644 --- a/src/app_modules/donasi/create/create_cerita_penggalang.tsx +++ b/src/app_modules/donasi/create/create_cerita_penggalang.tsx @@ -272,7 +272,7 @@ export default function CreateCeritaPenggalangDonasi({ }, }} withAsterisk - placeholder="Maskuan nomor rekening" + placeholder="Masukan nomor rekening" label="Nomor rekening" maxLength={100} onChange={(val) => { diff --git a/src/app_modules/donasi/create/create_donasi_new.tsx b/src/app_modules/donasi/create/create_donasi_new.tsx new file mode 100644 index 00000000..85389da1 --- /dev/null +++ b/src/app_modules/donasi/create/create_donasi_new.tsx @@ -0,0 +1,264 @@ +"use client"; +import { DIRECTORY_ID } from "@/app/lib"; +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { MainColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_BoxUploadImage } from "@/app_modules/_global/component"; +import ComponentGlobal_BoxInformation from "@/app_modules/_global/component/box_information"; +import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; +import { AspectRatio, Button, FileButton, Group, Image, Select, Stack, Text, TextInput, } from "@mantine/core"; +import { IconCamera, IconUpload } from "@tabler/icons-react"; +import { useAtom } from "jotai"; +import _ from "lodash"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import Donasi_funCreateTemporary from "../fun/create/fun_create_donasi_temporary"; +import { gs_donasi_tabs_posting } from "../global_state"; +import { apiGetMasterDonasi } from "../lib/api_donasi"; +import { useShallowEffect } from "@mantine/hooks"; + +export default function CreateDonasiNew() { + const router = useRouter(); + const [loadingMaster, setLoadingMaster] = useState(true) + const [isLoading, setLoading] = useState(false); + const [kategori, setKategori] = useState([]); + const [durasi, setDurasi] = useState([]); + const [data, setData] = useState({ + kategoriId: "", + title: "", + target: "", + durasiId: "", + }); + const [targetDana, setTargetDana] = useState(""); + const [file, setFile] = useState(null); + const [img, setImg] = useState(); + const [tabsPostingDonasi, setTabsPostingDonasi] = useAtom( + gs_donasi_tabs_posting + ); + + async function onGetMaster() { + try { + setLoadingMaster(true) + const responseKategori = await apiGetMasterDonasi("?cat=kategori") + const responseDurasi = await apiGetMasterDonasi("?cat=durasi") + if (responseKategori.success) { + setKategori(responseKategori.data) + } + if (responseDurasi.success) { + setDurasi(responseDurasi.data) + } + } catch (error) { + console.log(error); + } finally { + setLoadingMaster(false) + } + } + + useShallowEffect(() => { + onGetMaster() + }, []) + + async function onCreate() { + const body = { + donasiMaster_KategoriId: data.kategoriId, + donasiMaster_DurasiId: data.durasiId, + title: data.title, + target: targetDana, + }; + + if (_.values(body).includes("")) + return ComponentGlobal_NotifikasiPeringatan("Lengkapin Data"); + + try { + setLoading(true); + + const uploadImage = await funGlobal_UploadToStorage({ + file: file as File, + dirId: DIRECTORY_ID.donasi_image, + }); + if (!uploadImage.success) { + setLoading(false); + return ComponentGlobal_NotifikasiPeringatan("Gagal upload file gambar"); + } + + const res = await Donasi_funCreateTemporary({ + data: body as any, + fileId: uploadImage.data.id, + }); + if (res.status === 201) { + setTabsPostingDonasi("Review"); + router.push(RouterDonasi.create_cerita_penggalang + `${res.donasiId}`); + } else { + ComponentGlobal_NotifikasiGagal(res.message); + setLoading(false); + } + } catch (error) { + console.log(error); + } + } + + return ( + <> + + + ({ + value: e.id, + label: e.name + " " + `hari`, + }))} + onChange={(val: string) => setData({ ...data, durasiId: val })} + /> + + + + + {img ? ( + + Foto + + ) : ( + + + + Upload Gambar + + + )} + + + {/* Upload Foto */} + + { + try { + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + setImg(buffer); + setFile(files); + } catch (error) { + console.log(error); + } + }} + accept="image/png,image/jpeg" + > + {(props) => ( + + )} + + + + + + + + ); +} diff --git a/src/app_modules/donasi/index.ts b/src/app_modules/donasi/index.ts index 62da8ec0..f5d776c2 100644 --- a/src/app_modules/donasi/index.ts +++ b/src/app_modules/donasi/index.ts @@ -5,6 +5,7 @@ import GalangDanaDonasi from "./main/galang_dana/ui_galang_dana"; import GalangDanaDonasiNew from "./main/galang_dana/ui_galang_dana_new"; import DonasiSayaDonasi from "./main/donasi_saya"; import CreateDonasi from "./create/create_donasi"; +import CreateDonasiNew from "./create/create_donasi_new"; import LayoutCreateDonasi from "./create/layout"; import DetailMainDonasi from "./detail/detail_main"; import LayoutDetailMainDonasi from "./detail/detail_main/layout"; @@ -103,4 +104,5 @@ export { LayoutDonasi_EditRekening, MainDonasiNew, GalangDanaDonasiNew, + CreateDonasiNew }; diff --git a/src/app_modules/donasi/lib/api_donasi.ts b/src/app_modules/donasi/lib/api_donasi.ts index 36e2f5f6..04f661f3 100644 --- a/src/app_modules/donasi/lib/api_donasi.ts +++ b/src/app_modules/donasi/lib/api_donasi.ts @@ -1,4 +1,9 @@ export const apiGetAllDonasi = async (path?: string) => { const response = await fetch(`/api/new/donasi${(path) ? path : ''}`) return await response.json().catch(() => null) +} + +export const apiGetMasterDonasi = async (path?: string) => { + const response = await fetch(`/api/new/donasi/master${(path) ? path : ''}`) + return await response.json().catch(() => null) } \ No newline at end of file From 7cbbcd037038b4522f599c3b5e9693ad9e78b8ae Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Fri, 13 Dec 2024 07:37:31 +0800 Subject: [PATCH 062/595] Fix upload image profile and portofolio --- src/app/api/job/get-all/route.ts | 100 ++++- src/app/page.tsx | 4 +- .../button/comp_button_upload_photo.tsx | 1 + .../fun/upload/fun_upload_to_storage.ts | 2 + .../_global/ui/ui_image_preview.tsx | 6 +- src/app_modules/home/component/body_home.tsx | 349 +++++++++--------- .../home/component/footer_home.tsx | 200 +++++----- src/app_modules/home/view_home_new.tsx | 202 +++++----- .../job/main/beranda/ui_beranda.tsx | 124 ------- .../job/main/beranda/view_beranda.tsx | 118 +++++- .../button/comp_button_selanjutnya.tsx | 52 ++- .../katalog/portofolio/create/view.tsx | 63 +++- .../button/comp_create_new_profile.tsx | 25 -- .../katalog/profile/create/view.tsx | 65 +++- .../button/comp_button_save_pin.tsx | 56 ++- src/app_modules/map/ui/ui_create_pin.tsx | 65 +++- 16 files changed, 824 insertions(+), 608 deletions(-) delete mode 100644 src/app_modules/job/main/beranda/ui_beranda.tsx 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/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_modules/_global/button/comp_button_upload_photo.tsx b/src/app_modules/_global/button/comp_button_upload_photo.tsx index 0a28c023..8e56d342 100644 --- a/src/app_modules/_global/button/comp_button_upload_photo.tsx +++ b/src/app_modules/_global/button/comp_button_upload_photo.tsx @@ -24,6 +24,7 @@ export function ComponentGlobal_ButtonUploadFileImage({ if (files.size > MAX_SIZE) { ComponentGlobal_NotifikasiPeringatan(PemberitahuanMaksimalFile); + return; } else { onSetFile(files); onSetImage(buffer); 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..df5b7377 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 @@ -54,6 +54,8 @@ export async function funGlobal_UploadToStorage({ if (res.ok) { const dataRes = await res.json(); + // const cekLog = await res.text(); + // console.log(cekLog); return { success: true, data: dataRes.data }; } else { const errorText = await res.text(); 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/home/component/body_home.tsx b/src/app_modules/home/component/body_home.tsx index d5984b8d..69f7dac2 100644 --- a/src/app_modules/home/component/body_home.tsx +++ b/src/app_modules/home/component/body_home.tsx @@ -1,7 +1,17 @@ import { AccentColor, MainColor } from "@/app_modules/_global/color"; import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; -import { ActionIcon, Box, Group, Image, Paper, SimpleGrid, Skeleton, Stack, Text } from "@mantine/core"; +import { + ActionIcon, + Box, + Group, + Image, + Paper, + SimpleGrid, + Skeleton, + Stack, + Text, +} from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { IconUserSearch } from "@tabler/icons-react"; import _ from "lodash"; @@ -9,185 +19,182 @@ import { useRouter } from "next/navigation"; import { useState } from "react"; import { apiGetDataHome } from "../fun/get/api_home"; import { listMenuHomeBody, menuHomeJob } from "./list_menu_home"; +import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; export default function BodyHome() { - const router = useRouter() - const [dataUser, setDataUser] = useState({}) - const [dataJob, setDataJob] = useState([]) - const [loadingJob, setLoadingJob] = useState(true) + const router = useRouter(); + const [dataUser, setDataUser] = useState({}); + const [dataJob, setDataJob] = useState([]); + const [loadingJob, setLoadingJob] = useState(true); - useShallowEffect(() => { - cekUserLogin() - getHomeJob() - }, []); + useShallowEffect(() => { + cekUserLogin(); + getHomeJob(); + }, []); - - async function cekUserLogin() { - try { - const response = await apiGetDataHome("?cat=cek_profile") - if (response.success) { - setDataUser(response.data); - } - } catch (error) { - console.error(error); + async function cekUserLogin() { + try { + const response = await apiGetDataHome("?cat=cek_profile"); + if (response.success) { + setDataUser(response.data); } - } + } catch (error) { + console.error(error); + } + } - async function getHomeJob() { - try { - setLoadingJob(true) - const response = await apiGetDataHome("?cat=job") - if (response.success) { - setDataJob(response.data); - } - } catch (error) { - console.error(error); - } finally { - setLoadingJob(false) + async function getHomeJob() { + try { + setLoadingJob(true); + const response = await apiGetDataHome("?cat=job"); + if (response.success) { + setDataJob(response.data); } - } + } catch (error) { + console.error(error); + } finally { + setLoadingJob(false); + } + } - return ( - - - logo - + return ( + + + logo + - - - {listMenuHomeBody.map((e, i) => ( - { - if (dataUser.profile === undefined || dataUser?.profile === null) { - return ComponentGlobal_NotifikasiPeringatan( - "Lengkapi Profile" - ); - } else { - if (e.link === "") { - return ComponentGlobal_NotifikasiPeringatan( - "Cooming Soon !!" - ); - } else { - router.push(e.link, { scroll: false }); - } - } - }} - > - - - {e.icon} - - - {e.name} - - - - ))} - - - {/* Job View */} + + + {listMenuHomeBody.map((e, i) => ( - { - if (dataUser.profile === undefined || dataUser?.profile === null) { - return ComponentGlobal_NotifikasiPeringatan( - "Lengkapi Profile" - ); - } else { - if (menuHomeJob.link === "") { - return ComponentGlobal_NotifikasiPeringatan( - "Cooming Soon !!" - ); - } else { - return router.push(menuHomeJob.link, { scroll: false }); - } - } - }} - > - - - {menuHomeJob.icon} - - - {menuHomeJob.name} - - - { - loadingJob ? - Array(2) - .fill(null) - .map((_, i) => ( - - - - - - )) - : _.isEmpty(dataJob) ? - () - : ( - - {dataJob.map((e, i) => ( - - - - - - - - {e?.Author.username} - - - {e?.title} - - - - - ))} - - ) + key={e.id} + h={150} + bg={MainColor.darkblue} + style={{ + borderRadius: "10px 10px 10px 10px", + border: `2px solid ${AccentColor.blue}`, + }} + onClick={() => { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + if (e.link === "") { + return ComponentGlobal_NotifikasiPeringatan( + "Cooming Soon !!" + ); + } else { + router.push(e.link, { scroll: false }); } - + } + }} + > + + + {e.icon} + + + {e.name} + + - - - ); -} \ No newline at end of file + ))} + + + {/* Job View */} + + { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + if (menuHomeJob.link === "") { + return ComponentGlobal_NotifikasiPeringatan( + "Cooming Soon !!" + ); + } else { + return router.push(menuHomeJob.link, { scroll: false }); + } + } + }} + > + + + {menuHomeJob.icon} + + + {menuHomeJob.name} + + + {loadingJob ? ( + Array(2) + .fill(null) + .map((_, i) => ( + + + + + + )) + ) : _.isEmpty(dataJob) ? ( + + ) : ( + + {dataJob.map((e, i) => ( + + + + + + + + {e?.Author.username} + + + {e?.title} + + + + + ))} + + )} + + + + + ); +} diff --git a/src/app_modules/home/component/footer_home.tsx b/src/app_modules/home/component/footer_home.tsx index f116b19d..e1503ffa 100644 --- a/src/app_modules/home/component/footer_home.tsx +++ b/src/app_modules/home/component/footer_home.tsx @@ -1,7 +1,14 @@ import { APIs } from "@/app/lib"; import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; -import { ActionIcon, Box, Center, SimpleGrid, Stack, Text } from "@mantine/core"; +import { + ActionIcon, + Box, + Center, + SimpleGrid, + Stack, + Text, +} from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { IconUserCircle } from "@tabler/icons-react"; import { useRouter } from "next/navigation"; @@ -11,103 +18,104 @@ import { Home_ComponentAvatarProfile } from "./comp_avatar_profile"; import { listMenuHomeFooter } from "./list_menu_home"; export default function FooterHome() { - const router = useRouter() - const [dataUser, setDataUser] = useState({}) + const router = useRouter(); + const [dataUser, setDataUser] = useState({}); - useShallowEffect(() => { - cekUserLogin(); - }, []); + useShallowEffect(() => { + cekUserLogin(); + }, []); - - async function cekUserLogin() { - try { - const response = await apiGetDataHome("?cat=cek_profile") - if (response.success) { - setDataUser(response.data); - } - } catch (error) { - console.error(error); + async function cekUserLogin() { + try { + const response = await apiGetDataHome("?cat=cek_profile"); + if (response.success) { + setDataUser(response.data); } - } + } catch (error) { + console.error(error); + } + } - return ( - - - {listMenuHomeFooter.map((e) => ( -
- { - if (dataUser.profile === undefined || dataUser?.profile === null) { - ComponentGlobal_NotifikasiPeringatan("Lengkapi Profile"); - } else { - if (e.link == "") { - ComponentGlobal_NotifikasiPeringatan("Cooming Soon") - } else { - router.push(e.link, { scroll: false }) - } - } - }} - > - - {e.icon} - - - {e.name} - - -
- ))} + return ( + + + {listMenuHomeFooter.map((e) => ( +
+ { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + if (e.link == "") { + ComponentGlobal_NotifikasiPeringatan("Cooming Soon"); + } else { + router.push(e.link, { scroll: false }); + } + } + }} + > + + {e.icon} + + + {e.name} + + +
+ ))} -
- { - if (dataUser.profile === undefined || dataUser?.profile === null) { - router.push(RouterProfile.create, { scroll: false }); - } else { - router.push( - RouterProfile.katalogOLD + `${dataUser?.profile}`, - { scroll: false } - ); - } - }} - > - - {dataUser.profile === undefined || dataUser?.profile === null - ? - - : - - } - - - Profile - - -
-
-
- ); -} \ No newline at end of file +
+ { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + router.push(RouterProfile.katalogOLD + `${dataUser?.profile}`, { + scroll: false, + }); + } + }} + > + + {dataUser.profile === undefined || dataUser?.profile === null ? ( + + ) : ( + + )} + + + Profile + + +
+
+
+ ); +} diff --git a/src/app_modules/home/view_home_new.tsx b/src/app_modules/home/view_home_new.tsx index 8fbe5441..de88ed02 100644 --- a/src/app_modules/home/view_home_new.tsx +++ b/src/app_modules/home/view_home_new.tsx @@ -16,112 +16,120 @@ import notifikasi_countUserNotifikasi from "../notifikasi/fun/count/fun_count_by import BodyHome from "./component/body_home"; import FooterHome from "./component/footer_home"; import { apiGetDataHome } from "./fun/get/api_home"; +import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; -export default function HomeViewNew({ countNotifikasi }: { countNotifikasi: number; }) { - const [countNtf, setCountNtf] = useState(countNotifikasi); - const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); - const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); - const [dataUser, setDataUser] = useState({}) - const router = useRouter(); +export default function HomeViewNew({ + countNotifikasi, +}: { + countNotifikasi: number; +}) { + const [countNtf, setCountNtf] = useState(countNotifikasi); + const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); + const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); + const [dataUser, setDataUser] = useState({}); + const router = useRouter(); + useShallowEffect(() => { + onLoadNotifikasi({ + onLoad(val) { + setCountNtf(val); + }, + }); - useShallowEffect(() => { - onLoadNotifikasi({ - onLoad(val) { - setCountNtf(val); - }, - }); + setCountNtf(countLoadNtf as any); + }, [countLoadNtf, setCountNtf]); - setCountNtf(countLoadNtf as any); - }, [countLoadNtf, setCountNtf]); + useShallowEffect(() => { + setCountNtf(countNtf + newUserNtf); + setNewUserNtf(0); + }, [newUserNtf, setCountNtf]); - useShallowEffect(() => { - setCountNtf(countNtf + newUserNtf); - setNewUserNtf(0); - }, [newUserNtf, setCountNtf]); + async function onLoadNotifikasi({ onLoad }: { onLoad: (val: any) => void }) { + const loadNotif = await notifikasi_countUserNotifikasi(); + onLoad(loadNotif); + } - async function onLoadNotifikasi({ onLoad }: { onLoad: (val: any) => void }) { - const loadNotif = await notifikasi_countUserNotifikasi(); - onLoad(loadNotif); - } + useShallowEffect(() => { + cekUserLogin(); + }, []); - useShallowEffect(() => { - cekUserLogin(); - }, []); - - - async function cekUserLogin() { - try { - const response = await apiGetDataHome("?cat=cek_profile") - if (response.success) { - setDataUser(response.data); - } - } catch (error) { - console.error(error); + async function cekUserLogin() { + try { + const response = await apiGetDataHome("?cat=cek_profile"); + if (response.success) { + setDataUser(response.data); } - } + } catch (error) { + console.error(error); + } + } - return ( - <> - { - if (dataUser.profile === undefined || dataUser?.profile === null) { - ComponentGlobal_NotifikasiPeringatan("Lengkapi Profile"); - } else { - router.push(RouterUserSearch.main, { scroll: false }); - } - }} - > - - + return ( + <> + { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + router.push(RouterUserSearch.main, { scroll: false }); } - customButtonRight={ - { - if (dataUser.profile === undefined || dataUser?.profile === null) { - ComponentGlobal_NotifikasiPeringatan("Lengkapi Profile"); - } else { - router.push(RouterNotifikasi.categoryApp({ name: "semua" }), { - scroll: false, - }); - } - }} - > - { - countNotifikasi > 0 - ? - - {countNotifikasi > 99 ? "99+" : countNotifikasi} - - } - > - - - : - - } - - - } - /> + }} + > + + } - - footer={} - > - - - - ); + customButtonRight={ + { + if ( + dataUser.profile === undefined || + dataUser?.profile === null + ) { + router.push(RouterProfile.create, { scroll: false }); + } else { + router.push( + RouterNotifikasi.categoryApp({ name: "semua" }), + { + scroll: false, + } + ); + } + }} + > + {countNotifikasi > 0 ? ( + + {countNotifikasi > 99 ? "99+" : countNotifikasi} + + } + > + + + ) : ( + + )} + + } + /> + } + footer={} + > + + + + ); } diff --git a/src/app_modules/job/main/beranda/ui_beranda.tsx b/src/app_modules/job/main/beranda/ui_beranda.tsx deleted file mode 100644 index 0783e9f7..00000000 --- a/src/app_modules/job/main/beranda/ui_beranda.tsx +++ /dev/null @@ -1,124 +0,0 @@ -"use client"; - -import { gs_jobTiggerBeranda } from "@/app/lib/global_state"; -import { RouterJob } from "@/app/lib/router_hipmi/router_job"; -import ComponentGlobal_CreateButton from "@/app_modules/_global/component/button_create"; -import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; -import { Box, Center, Loader, Stack, TextInput } from "@mantine/core"; -import { useShallowEffect } from "@mantine/hooks"; -import { IconSearch } from "@tabler/icons-react"; -import { useAtom } from "jotai"; -import _ from "lodash"; -import { ScrollOnly } from "next-scroll-loader"; -import { useState } from "react"; -import { - Job_ComponentButtonUpdateBeranda, - Job_ComponentSkeletonBeranda, -} from "../../component"; -import ComponentJob_BerandaCardView from "../../component/beranda/card_view"; -import { job_getAllListPublish } from "../../fun/get/get_all_publish"; -import { MODEL_JOB } from "../../model/interface"; -import { API_RouteJob } from "@/app/lib/api_user_router/route_api_job"; - -export function Job_UiBeranda() { - const [data, setData] = useState(null); - const [activePage, setActivePage] = useState(1); - const [isSearch, setIsSearch] = useState(""); - - // Notifikasi - const [isShowUpdate, setIsShowUpdate] = useState(false); - const [isTriggerJob, setIsTriggerJob] = useAtom(gs_jobTiggerBeranda); - - useShallowEffect(() => { - if (isTriggerJob == true) { - setIsShowUpdate(true); - } - }, [isTriggerJob]); - - useShallowEffect(() => { - setIsTriggerJob(false); - setIsShowUpdate(false); - onLoadNewData(); - }, []); - - async function onSearch(text: string) { - setIsSearch(text); - const loadData = await job_getAllListPublish({ - page: activePage, - search: text, - }); - setData(loadData as any); - setActivePage(1); - } - - async function onLoadNewData() { - const loadData = await fetch(API_RouteJob.get_all({ page: activePage })); - const res = await loadData.json(); - - setData(res.data); - } - - return ( - <> - - {isShowUpdate && ( - { - setIsShowUpdate(val); - setIsTriggerJob(val); - }} - onSetData={(val: any[]) => { - setData(val); - }} - /> - )} - - - - } - placeholder="Pekerjaan apa yang anda cari ?" - onChange={(val) => { - onSearch(val.currentTarget.value); - }} - /> - - {_.isNull(data) ? ( - - ) : _.isEmpty(data) ? ( - - ) : ( - // --- Main component --- // - ( -
- -
- )} - data={data} - setData={setData as any} - moreData={async () => { - const loadData = await job_getAllListPublish({ - page: activePage + 1, - search: isSearch, - }); - - setActivePage((val) => val + 1); - - return loadData; - }} - > - {(item) => } -
- )} -
- - ); -} diff --git a/src/app_modules/job/main/beranda/view_beranda.tsx b/src/app_modules/job/main/beranda/view_beranda.tsx index d6004905..f9c905b5 100644 --- a/src/app_modules/job/main/beranda/view_beranda.tsx +++ b/src/app_modules/job/main/beranda/view_beranda.tsx @@ -1,10 +1,124 @@ +"use client"; + +import { API_RouteJob } from "@/app/lib/api_user_router/route_api_job"; +import { gs_jobTiggerBeranda } from "@/app/lib/global_state"; +import { RouterJob } from "@/app/lib/router_hipmi/router_job"; +import ComponentGlobal_CreateButton from "@/app_modules/_global/component/button_create"; +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import { Center, Loader, Stack, TextInput } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconSearch } from "@tabler/icons-react"; +import { useAtom } from "jotai"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useState } from "react"; +import { + Job_ComponentButtonUpdateBeranda, + Job_ComponentSkeletonBeranda, +} from "../../component"; +import ComponentJob_BerandaCardView from "../../component/beranda/card_view"; import { MODEL_JOB } from "../../model/interface"; -import { Job_UiBeranda } from "./ui_beranda"; export default function Job_ViewBeranda() { + const [data, setData] = useState(null); + const [activePage, setActivePage] = useState(1); + const [isSearch, setIsSearch] = useState(""); + + // Notifikasi + const [isShowUpdate, setIsShowUpdate] = useState(false); + const [isTriggerJob, setIsTriggerJob] = useAtom(gs_jobTiggerBeranda); + + useShallowEffect(() => { + if (isTriggerJob == true) { + setIsShowUpdate(true); + } + }, [isTriggerJob]); + + useShallowEffect(() => { + setIsTriggerJob(false); + setIsShowUpdate(false); + onLoadNewData(); + }, []); + + async function onSearch(text: string) { + setIsSearch(text); + const loadData = await fetch( + API_RouteJob.get_all({ page: activePage, search: text }) + ); + const res = await loadData.json(); + + setData(res.data as any); + setActivePage(1); + } + + async function onLoadNewData() { + const loadData = await fetch(API_RouteJob.get_all({ page: activePage })); + const res = await loadData.json(); + // console.log(res.data); + setData(res.data); + } + return ( <> - + + {isShowUpdate && ( + { + setIsShowUpdate(val); + setIsTriggerJob(val); + }} + onSetData={(val: any[]) => { + setData(val); + }} + /> + )} + + + + } + placeholder="Pekerjaan apa yang anda cari ?" + onChange={(val) => { + onSearch(val.currentTarget.value); + }} + /> + + {_.isNull(data) ? ( + + ) : _.isEmpty(data) ? ( + + ) : ( + // --- Main component --- // + ( +
+ +
+ )} + data={data} + setData={setData as any} + moreData={async () => { + const loadData = await fetch( + API_RouteJob.get_all({ page: activePage, search: isSearch }) + ); + + const res = await loadData.json(); + setActivePage((val) => val + 1); + + return res.data; + }} + > + {(item) => } +
+ )} +
); } diff --git a/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx b/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx index 41c037b9..6af70436 100644 --- a/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx +++ b/src/app_modules/katalog/portofolio/component/button/comp_button_selanjutnya.tsx @@ -12,25 +12,22 @@ import _ from "lodash"; import { useRouter } from "next/navigation"; import { useState } from "react"; import funCreatePortofolio from "../../fun/fun_create_portofolio"; -import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; -import { DIRECTORY_ID } from "@/app/lib"; export function Portofolio_ComponentButtonSelanjutnya({ profileId, dataPortofolio, - file, dataMedsos, + imageId, }: { profileId: string; dataPortofolio: MODEL_PORTOFOLIO_OLD; - file: File; dataMedsos: any; + imageId: string }) { const router = useRouter(); const [loading, setLoading] = useState(false); async function onSubmit() { - setLoading(true); const porto = { namaBisnis: dataPortofolio.namaBisnis, masterBidangBisnisId: dataPortofolio.masterBidangBisnisId, @@ -39,34 +36,33 @@ export function Portofolio_ComponentButtonSelanjutnya({ deskripsi: dataPortofolio.deskripsi, }; - if (_.values(porto).includes("")) - return ComponentGlobal_NotifikasiPeringatan("Lengkapi Data"); - - const uploadFileToStorage = await funGlobal_UploadToStorage({ - file: file, - dirId: DIRECTORY_ID.portofolio_logo, - }); - - if (!uploadFileToStorage.success) - return ComponentGlobal_NotifikasiPeringatan("Gagal upload gambar"); - - const res = await funCreatePortofolio({ - profileId: profileId, - data: dataPortofolio as any, - medsos: dataMedsos, - fileId: uploadFileToStorage.data.id, - }); - if (res.status === 201) { - ComponentGlobal_NotifikasiBerhasil("Berhasil disimpan"); - router.replace(RouterMap.create + res.id, { scroll: false }); - } else { - ComponentGlobal_NotifikasiGagal("Gagal disimpan"); + try { + setLoading(true); + if (_.values(porto).includes("")) { + return ComponentGlobal_NotifikasiPeringatan("Lengkapi Data"); + } + const res = await funCreatePortofolio({ + profileId: profileId, + data: dataPortofolio as any, + medsos: dataMedsos, + fileId: imageId, + }); + if (res.status === 201) { + ComponentGlobal_NotifikasiBerhasil("Berhasil disimpan"); + router.replace(RouterMap.create + res.id, { scroll: false }); + } else { + ComponentGlobal_NotifikasiGagal("Gagal disimpan"); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false); } } return ( <> + )} + + + + + {/* 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, + }); + }} + /> + + ({ + 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, + }); + }} + /> + + + + } + + + ); +} From 4dd98c6183c3ad8534f363ab6f80a05757f9e678 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 16 Dec 2024 13:58:27 +0800 Subject: [PATCH 067/595] chore(release): 1.2.30 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e67762c4..56cbce3a 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.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) diff --git a/package.json b/package.json index 22e9d9ef..5091e4d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.29", + "version": "1.2.30", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 63d6d48f639cf6054ce65afea40735839c2cd879 Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 16 Dec 2024 15:16:55 +0800 Subject: [PATCH 068/595] upd: investasi Deskripsi: - api pada bursa investasi No Issues --- src/app/api/new/investasi/route.ts | 112 ++++++++++++++++++ src/app/dev/investasi/main/page.tsx | 8 +- .../_component/main/com_card_beranda_new.tsx | 108 +++++++++++++++++ .../investasi/_lib/api_interface.ts | 5 + .../investasi/_lib/type_investasi.ts | 9 ++ src/app_modules/investasi/_ui/index.ts | 2 + .../investasi/_view/main/skeleton_beranda.tsx | 31 +++++ .../investasi/_view/main/view_beranda_new.tsx | 97 +++++++++++++++ 8 files changed, 368 insertions(+), 4 deletions(-) create mode 100644 src/app/api/new/investasi/route.ts create mode 100644 src/app_modules/investasi/_component/main/com_card_beranda_new.tsx create mode 100644 src/app_modules/investasi/_view/main/skeleton_beranda.tsx create mode 100644 src/app_modules/investasi/_view/main/view_beranda_new.tsx diff --git a/src/app/api/new/investasi/route.ts b/src/app/api/new/investasi/route.ts new file mode 100644 index 00000000..a8939023 --- /dev/null +++ b/src/app/api/new/investasi/route.ts @@ -0,0 +1,112 @@ +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 }); + } + } + + + + 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/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_modules/investasi/_component/main/com_card_beranda_new.tsx b/src/app_modules/investasi/_component/main/com_card_beranda_new.tsx new file mode 100644 index 00000000..02f66801 --- /dev/null +++ b/src/app_modules/investasi/_component/main/com_card_beranda_new.tsx @@ -0,0 +1,108 @@ +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { Warna } from "@/app/lib/warna"; +import { MainColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_CardStyles, ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; +import { Box, Grid, Group, Progress, Stack, Text } from "@mantine/core"; +import { IconCircleCheck, IconXboxX } from "@tabler/icons-react"; +import moment from "moment"; +import { useRouter } from "next/navigation"; +import { IDataInvestasiBursa } from "../../_lib/type_investasi"; + +export function Investasi_ComponentCardBerandaNew({ data }: { data: IDataInvestasiBursa; }) { + const router = useRouter() + + return ( + <> + { + router.push(NEW_RouterInvestasi.detail_main({ id: data.id }), { + scroll: false, + }); + }} + > + + + + + + + + + {data?.title} + + + + + {data?.progress === "100" ? ( + + + + Selesai + + + ) : ( + + {+data?.pencarianInvestor - + moment(new Date()).diff( + new Date(data?.countDown), + "days" + ) <= + 0 ? ( + + + + Waktu Habis + + + ) : ( + + Sisa waktu: + + {Number(data?.pencarianInvestor) - + moment(new Date()).diff( + new Date(data?.countDown), + "days" + )} + + Hari + + )} + + )} + + + + + + + + ); +} diff --git a/src/app_modules/investasi/_lib/api_interface.ts b/src/app_modules/investasi/_lib/api_interface.ts index 0dbb9c42..eb9aafda 100644 --- a/src/app_modules/investasi/_lib/api_interface.ts +++ b/src/app_modules/investasi/_lib/api_interface.ts @@ -6,4 +6,9 @@ export const apiGetMasterInvestasi = async (path?: string) => { export const apiGetOneInvestasiById = async (path: string) => { const response = await fetch(`/api/new/investasi/${path}`) return await response.json().catch(() => null) +} + +export const apiGetAllInvestasi = async (path?: string) => { + const response = await fetch(`/api/new/investasi${(path) ? path : ''}`) + return await response.json().catch(() => null) } \ No newline at end of file diff --git a/src/app_modules/investasi/_lib/type_investasi.ts b/src/app_modules/investasi/_lib/type_investasi.ts index 8972629d..b794fb1b 100644 --- a/src/app_modules/investasi/_lib/type_investasi.ts +++ b/src/app_modules/investasi/_lib/type_investasi.ts @@ -12,4 +12,13 @@ export interface IDataInvestasi { masterPencarianInvestorId: string masterPeriodeDevidenId: string masterPembagianDevidenId: string +} + +export interface IDataInvestasiBursa { + id: string + title: string + imageId: string + progress: string + countDown: string + pencarianInvestor: string } \ No newline at end of file diff --git a/src/app_modules/investasi/_ui/index.ts b/src/app_modules/investasi/_ui/index.ts index 39103b48..6d51522c 100644 --- a/src/app_modules/investasi/_ui/index.ts +++ b/src/app_modules/investasi/_ui/index.ts @@ -25,6 +25,7 @@ import { Investasi_UiRekapBerita } from "./detail/ui_rekap_berita"; import { Investasi_UiCreateBerita } from "./create/ui_create_berita"; import { Investasi_UiDetailBerita } from "./detail/ui_berita"; import { Investasi_UiEditInvestasiNew } from "./edit/ui_edit_investasi_new"; +import { Investasi_ViewBerandaNew } from "../_view/main/view_beranda_new"; export { Investasi_UiProsesPembelian }; export { Investasi_UiMetodePembayaran }; @@ -53,3 +54,4 @@ export { Investasi_UiRekapBerita }; export { Investasi_UiCreateBerita }; export { Investasi_UiDetailBerita }; export { Investasi_UiEditInvestasiNew } +export { Investasi_ViewBerandaNew } 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/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) => } +
+ ) + + } +
+ + ); +} From 0bdc25b1e62d6d95307002a0e2af1f58948c14e9 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 16 Dec 2024 15:41:02 +0800 Subject: [PATCH 069/595] Feature logs Deskripsi: - Fitur baru log untuk melihat error pada server upload - Baru di terapkan di create profile --- .gitignore | 3 + bun.lockb | Bin 708596 -> 716668 bytes src/app/api/image/delete/route.ts | 53 ++++ src/app/api/image/upload/route.ts | 58 ++++ src/app/api/upload/route.ts | 51 ---- src/app/zCoba/page.tsx | 2 +- .../fun/delete/fun_delete_file_by_id.tsx | 60 ++-- .../_global/fun/get/fun_get_directory_name.ts | 11 + src/app_modules/_global/fun/get/index.ts | 3 +- .../fun/upload/fun_upload_to_storage.ts | 38 +-- .../katalog/profile/create/view.tsx | 264 ++---------------- .../profile/create/view_upload_background.tsx | 170 +++++++++++ .../profile/create/view_upload_foto.tsx | 216 ++++++++++++++ .../katalog/profile/fun/fun_create_profile.ts | 5 +- src/app_modules/zCoba/ui_coba_upload_file.tsx | 2 +- src/middleware.ts | 3 +- src/util/backendLogger.ts | 37 +++ src/util/clientLogger.ts | 87 ++++++ src/util/frontend-logger.ts | 101 +++++++ 19 files changed, 815 insertions(+), 349 deletions(-) create mode 100644 src/app/api/image/delete/route.ts create mode 100644 src/app/api/image/upload/route.ts delete mode 100644 src/app/api/upload/route.ts create mode 100644 src/app_modules/_global/fun/get/fun_get_directory_name.ts create mode 100644 src/app_modules/katalog/profile/create/view_upload_background.tsx create mode 100644 src/app_modules/katalog/profile/create/view_upload_foto.tsx create mode 100644 src/util/backendLogger.ts create mode 100644 src/util/clientLogger.ts create mode 100644 src/util/frontend-logger.ts diff --git a/.gitignore b/.gitignore index 61b4bc33..924563ab 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/bun.lockb b/bun.lockb index 88eaa6ad3e0e2d12a92a93c78751e54fe7ad9b20..f486e079beb2452bdedf0d7c49d7fd7dbd4d4ed8 100755 GIT binary patch delta 148191 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�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

Z{*gir0zks0+0())6gFv(3 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 VrgI168B~&GQ#mvFfXzkk{{ru-zYYKZ 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/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/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/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 df5b7377..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,40 +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(); - // const cekLog = await res.text(); - // console.log(cekLog); - 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/katalog/profile/create/view.tsx b/src/app_modules/katalog/profile/create/view.tsx index f0dd96b4..90d51ab2 100644 --- a/src/app_modules/katalog/profile/create/view.tsx +++ b/src/app_modules/katalog/profile/create/view.tsx @@ -1,42 +1,16 @@ "use client"; -import { DIRECTORY_ID } from "@/app/lib"; -import { MainColor } from "@/app_modules/_global/color"; -import { - ComponentGlobal_BoxInformation, - ComponentGlobal_BoxUploadImage, - ComponentGlobal_ErrorInput, -} from "@/app_modules/_global/component"; -import { - funGlobal_DeleteFileById, - funGlobal_UploadToStorage, -} from "@/app_modules/_global/fun"; -import { MAX_SIZE } from "@/app_modules/_global/lib"; -import { PemberitahuanMaksimalFile } from "@/app_modules/_global/lib/max_size"; -import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; -import { - AspectRatio, - Avatar, - Box, - Button, - Center, - FileButton, - Image, - Paper, - Select, - Stack, - Text, - TextInput, -} from "@mantine/core"; -import { IconAt, IconCamera, IconUpload } from "@tabler/icons-react"; +import { ComponentGlobal_ErrorInput } from "@/app_modules/_global/component"; +import { Select, Stack, TextInput } from "@mantine/core"; +import { IconAt } from "@tabler/icons-react"; import { useState } from "react"; import { gmailRegex } from "../../component/regular_expressions"; import { Profile_ComponentCreateNewProfile } from "../_component"; +import Profile_ViewUploadBackground from "./view_upload_background"; +import Profile_ViewUploadFoto from "./view_upload_foto"; export default function CreateProfile() { - const [filePP, setFilePP] = useState(null); const [imgPP, setImgPP] = useState(); - const [fileBG, setFileBG] = useState(null); const [imgBG, setImgBG] = useState(); const [fotoProfileId, setFotoProfileId] = useState(""); const [backgroundProfileId, setBackgroundProfileId] = useState(""); @@ -51,223 +25,19 @@ export default function CreateProfile() { return ( <> - - - -

- {imgPP ? ( - - - - ) : ( - - - - )} -
+ -
- { - try { - const buffer = URL.createObjectURL( - new Blob([new Uint8Array(await files.arrayBuffer())]) - ); - - if (files.size > MAX_SIZE) { - ComponentGlobal_NotifikasiPeringatan( - PemberitahuanMaksimalFile - ); - setImgPP(null); - setFilePP(null); - - return; - } - - if (fotoProfileId != "") { - const deleteFotoProfile = await funGlobal_DeleteFileById({ - fileId: fotoProfileId, - }); - - if (deleteFotoProfile.success) { - setFotoProfileId(""); - - const uploadPhoto = await funGlobal_UploadToStorage({ - file: files, - dirId: DIRECTORY_ID.profile_foto, - }); - - if (uploadPhoto.success) { - setFotoProfileId(uploadPhoto.data.id); - setImgPP(buffer); - setFilePP(files); - } else { - ComponentGlobal_NotifikasiPeringatan( - "Gagal upload foto profile" - ); - } - } - } else { - const uploadPhoto = await funGlobal_UploadToStorage({ - file: files, - dirId: DIRECTORY_ID.profile_foto, - }); - - if (uploadPhoto.success) { - setFotoProfileId(uploadPhoto.data.id); - setImgPP(buffer); - setFilePP(files); - } else { - ComponentGlobal_NotifikasiPeringatan( - "Gagal upload foto profile" - ); - } - } - } catch (error) { - console.log(error); - } - }} - accept="image/png,image/jpeg" - > - {(props) => ( - - )} - -
- - - - - - - - {imgBG ? ( - - Foto - - ) : ( - - - - Upload Background - - - )} - - -
- { - try { - const buffer = URL.createObjectURL( - new Blob([new Uint8Array(await files.arrayBuffer())]) - ); - - if (files.size > MAX_SIZE) { - ComponentGlobal_NotifikasiPeringatan( - PemberitahuanMaksimalFile - ); - setImgBG(null); - setFileBG(null); - return; - } - - if (backgroundProfileId != "") { - const deleteFotoBg = await funGlobal_DeleteFileById({ - fileId: backgroundProfileId, - }); - - if (deleteFotoBg.success) { - setBackgroundProfileId(""); - - const uploadBackground = - await funGlobal_UploadToStorage({ - file: files, - dirId: DIRECTORY_ID.profile_background, - }); - - if (uploadBackground.success) { - setBackgroundProfileId(uploadBackground.data.id); - setImgBG(buffer); - setFileBG(files); - } else { - ComponentGlobal_NotifikasiPeringatan( - "Gagal upload background profile" - ); - } - } - } else { - const uploadBackground = await funGlobal_UploadToStorage({ - file: files, - dirId: DIRECTORY_ID.profile_background, - }); - - if (uploadBackground.success) { - setBackgroundProfileId(uploadBackground.data.id); - setImgBG(buffer); - setFileBG(files); - } else { - ComponentGlobal_NotifikasiPeringatan( - "Gagal upload background profile" - ); - } - } - } catch (error) { - console.log(error); - } - }} - accept="image/png,image/jpeg" - > - {(props) => ( - - )} - -
-
-
+ void; + backgroundProfileId: string; + onSetBackgroundProfileId: (id: string) => void; +}) { + const [isLoading, setLoading] = useState(false); + + return ( + <> + + + + + {isLoading ? ( +
+ +
+ ) : imgBG ? ( + + Foto + + ) : ( + + + + Upload Background + + + )} +
+ +
+ { + try { + setLoading(true); + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + + if (files.size > MAX_SIZE) { + ComponentGlobal_NotifikasiPeringatan( + PemberitahuanMaksimalFile + ); + onSetImgBG(null); + + return; + } + + if (backgroundProfileId != "") { + const deleteFotoBg = await funGlobal_DeleteFileById({ + fileId: backgroundProfileId, + dirId: DIRECTORY_ID.profile_background, + }); + + if (!deleteFotoBg.success) { + clientLogger.error( + "Client failed delete background:" + + deleteFotoBg.message + ); + return; + } + + if (deleteFotoBg.success) { + onSetBackgroundProfileId(""); + onSetImgBG(null); + + const uploadBackground = await funGlobal_UploadToStorage({ + file: files, + dirId: DIRECTORY_ID.profile_background, + }); + + if (!uploadBackground.success) { + clientLogger.error( + "Client failed upload background:" + + uploadBackground.message + ); + return; + } + + if (uploadBackground.success) { + onSetBackgroundProfileId(uploadBackground.data.id); + onSetImgBG(buffer); + } else { + ComponentGlobal_NotifikasiPeringatan( + "Gagal upload background profile" + ); + } + } + } else { + const uploadBackground = await funGlobal_UploadToStorage({ + file: files, + dirId: DIRECTORY_ID.profile_background, + }); + + if (uploadBackground.success) { + onSetBackgroundProfileId(uploadBackground.data.id); + onSetImgBG(buffer); + } else { + ComponentGlobal_NotifikasiPeringatan( + "Gagal upload background profile" + ); + } + } + } catch (error) { + clientLogger.error("Client error upload background:", error); + } finally { + setLoading(false); + } + }} + accept="image/png,image/jpeg" + > + {(props) => ( + + )} + +
+
+
+ + ); +} diff --git a/src/app_modules/katalog/profile/create/view_upload_foto.tsx b/src/app_modules/katalog/profile/create/view_upload_foto.tsx new file mode 100644 index 00000000..279f1282 --- /dev/null +++ b/src/app_modules/katalog/profile/create/view_upload_foto.tsx @@ -0,0 +1,216 @@ +import { DIRECTORY_ID } from "@/app/lib"; +import { MainColor } from "@/app_modules/_global/color"; +import { ComponentGlobal_BoxInformation } from "@/app_modules/_global/component"; +import { + funGlobal_DeleteFileById, + funGlobal_UploadToStorage, +} from "@/app_modules/_global/fun"; +import { MAX_SIZE } from "@/app_modules/_global/lib"; +import { PemberitahuanMaksimalFile } from "@/app_modules/_global/lib/max_size"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { clientLogger } from "@/util/clientLogger"; +import { + Avatar, + Box, + Button, + Center, + FileButton, + Loader, + Paper, + Stack, +} from "@mantine/core"; +import { IconCamera } from "@tabler/icons-react"; +import { useState } from "react"; + +export default function Profile_ViewUploadFoto({ + imgPP, + onSetImgPP, + fotoProfileId, + onSetFotoProfileId, +}: { + imgPP: string | null | undefined; + onSetImgPP: (img: string | null) => void; + fotoProfileId: string; + onSetFotoProfileId: (id: string) => void; +}) { + const [isLoading, setLoading] = useState(false); + + return ( + <> + + + +
+ {isLoading ? ( + + +
+ +
+
+
+ ) : imgPP != undefined || imgPP != null ? ( + + + + ) : ( + + + + )} +
+ +
+ { + try { + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + + if (files.size > MAX_SIZE) { + ComponentGlobal_NotifikasiPeringatan( + PemberitahuanMaksimalFile + ); + onSetImgPP(null); + + return; + } + + if (fotoProfileId != "") { + try { + setLoading(true); + const deleteFotoProfile = await funGlobal_DeleteFileById({ + fileId: fotoProfileId, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (!deleteFotoProfile.success) { + clientLogger.error( + "Client failed delete photo profile:" + + deleteFotoProfile.message + ); + + return; + } + + if (deleteFotoProfile.success) { + onSetFotoProfileId(""); + onSetImgPP(null); + + const uploadPhoto = await funGlobal_UploadToStorage({ + file: files, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (!uploadPhoto.success) { + clientLogger.error( + "Client failed upload photo profile::" + + uploadPhoto.message + ); + return; + } + + if (uploadPhoto.success) { + clientLogger.info( + "Client success upload foto profile" + ); + onSetFotoProfileId(uploadPhoto.data.id); + onSetImgPP(buffer); + } else { + clientLogger.error( + "Client failed upload foto:", + uploadPhoto.message + ); + ComponentGlobal_NotifikasiPeringatan( + "Gagal upload foto profile" + ); + } + } + } catch (error) { + clientLogger.error("Client error upload foto:", error); + } finally { + setLoading(false); + } + } else { + try { + setLoading(true); + const uploadPhoto = await funGlobal_UploadToStorage({ + file: files, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (uploadPhoto.success) { + clientLogger.info("Client success upload foto profile"); + onSetFotoProfileId(uploadPhoto.data.id); + onSetImgPP(buffer); + } else { + clientLogger.error( + "Client failed upload foto:", + uploadPhoto.message + ); + ComponentGlobal_NotifikasiPeringatan( + "Gagal upload foto profile" + ); + } + } catch (error) { + clientLogger.error("Client error upload foto:", error); + } finally { + setLoading(false); + } + } + } catch (error) { + clientLogger.error("Client error upload foto:", error); + } + }} + accept="image/png,image/jpeg" + > + {(props) => ( + + )} + +
+
+
+ + ); +} diff --git a/src/app_modules/katalog/profile/fun/fun_create_profile.ts b/src/app_modules/katalog/profile/fun/fun_create_profile.ts index ff8eb96d..35af0660 100644 --- a/src/app_modules/katalog/profile/fun/fun_create_profile.ts +++ b/src/app_modules/katalog/profile/fun/fun_create_profile.ts @@ -3,6 +3,7 @@ import prisma from "@/app/lib/prisma"; import { RouterHome } from "@/app/lib/router_hipmi/router_home"; import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import backendLogger from "@/util/backendLogger"; import { Prisma } from "@prisma/client"; import { revalidatePath } from "next/cache"; @@ -19,6 +20,7 @@ export default async function funCreateNewProfile({ const userLoginId = await funGetUserIdByToken(); if (!userLoginId) { + backendLogger.error("User tidak terautentikasi"); return { status: 400, message: "User tidak terautentikasi" }; // Validasi user login } @@ -45,6 +47,7 @@ export default async function funCreateNewProfile({ }); if (!createProfile) { + backendLogger.error("Gagal membuat profile"); return { status: 400, message: "Gagal membuat profile" }; } @@ -61,7 +64,7 @@ export default async function funCreateNewProfile({ message: "Berhasil", }; } catch (error) { - console.error("Error creating profile:", error); + backendLogger.error("Terjadi kesalahan pada server", error); return { status: 500, message: "Terjadi kesalahan pada server" }; } } diff --git a/src/app_modules/zCoba/ui_coba_upload_file.tsx b/src/app_modules/zCoba/ui_coba_upload_file.tsx index 091a09fa..eb6eb30e 100644 --- a/src/app_modules/zCoba/ui_coba_upload_file.tsx +++ b/src/app_modules/zCoba/ui_coba_upload_file.tsx @@ -189,7 +189,7 @@ async function coba_ButtonFileUpload({ formData.append("dirId", dirId); try { - const res = await fetch("https://wibu-storage.wibudev.com/api/upload", { + const res = await fetch("https://wibu-storage.wibudev.com/api/image/upload", { method: "POST", body: formData, headers: { diff --git a/src/middleware.ts b/src/middleware.ts index 705192f2..6a72d076 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -19,7 +19,8 @@ const middlewareConfig: MiddlewareConfig = { userPath: "/dev/home", publicRoutes: [ "/", - "/api/upload", + "/api/logs/*", + "/api/image/*", "/api/job/*", "/api/validation", "/api/auth/*", diff --git a/src/util/backendLogger.ts b/src/util/backendLogger.ts new file mode 100644 index 00000000..9a8e3d30 --- /dev/null +++ b/src/util/backendLogger.ts @@ -0,0 +1,37 @@ +// src/utils/backendLogger.ts +import winston from "winston"; +import DailyRotateFile from "winston-daily-rotate-file"; +import path from "path"; + +const backendLogger = winston.createLogger({ + level: "info", + format: winston.format.combine( + winston.format.timestamp(), + winston.format.json() + ), + transports: [ + // Error logs + new DailyRotateFile({ + filename: path.join("logs/backend/error-%DATE%.log"), + datePattern: "YYYY-MM-DD", + level: "error", + zippedArchive: true, + maxSize: "20m", + maxFiles: "14d", + }), + // Combined logs + new DailyRotateFile({ + filename: path.join("logs/backend/combined-%DATE%.log"), + datePattern: "YYYY-MM-DD", + zippedArchive: true, + maxSize: "20m", + maxFiles: "14d", + }), + // Console output in development + ...(process.env.NODE_ENV !== "production" + ? [new winston.transports.Console()] + : []), + ], +}); + +export default backendLogger; diff --git a/src/util/clientLogger.ts b/src/util/clientLogger.ts new file mode 100644 index 00000000..4782df23 --- /dev/null +++ b/src/util/clientLogger.ts @@ -0,0 +1,87 @@ +// src/utils/clientLogger.ts +interface LogEntry { + level: "info" | "warn" | "error"; + message: string; + data?: any; + timestamp?: string; +} + +class ClientLogger { + private queue: LogEntry[] = []; + private readonly maxQueueSize: number = 10; + private readonly apiEndpoint: string = "/api/logs"; + private isSending: boolean = false; + + private async sendLogs(): Promise { + if (this.isSending || this.queue.length === 0) return; + + this.isSending = true; + const logsToSend = [...this.queue]; + this.queue = []; + + try { + const response = await fetch(this.apiEndpoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(logsToSend), + }); + + if (!response.ok) { + console.error("Failed to send logs:", response.statusText); + // Restore logs to queue if send failed + this.queue = [...logsToSend, ...this.queue]; + } + } catch (error) { + console.error("Error sending logs:", error); + // Restore logs to queue if send failed + this.queue = [...logsToSend, ...this.queue]; + } finally { + this.isSending = false; + } + } + + private addToQueue(entry: LogEntry): void { + this.queue.push({ + ...entry, + timestamp: new Date().toISOString(), + }); + + if (this.queue.length >= this.maxQueueSize) { + this.sendLogs(); + } + } + + public info(message: string, data?: any): void { + this.addToQueue({ level: "info", message, data }); + } + + public warn(message: string, data?: any): void { + this.addToQueue({ level: "warn", message, data }); + // Send immediately for warnings + this.sendLogs(); + } + + public error(message: string, error?: Error | any): void { + const errorData = + error instanceof Error + ? { + name: error.name, + message: error.message, + stack: error.stack, + } + : error; + + this.addToQueue({ level: "error", message, data: errorData }); + // Send immediately for errors + this.sendLogs(); + } + + // Flush remaining logs (useful when page is about to unload) + public flush(): void { + this.sendLogs(); + } +} + +export const clientLogger = new ClientLogger(); diff --git a/src/util/frontend-logger.ts b/src/util/frontend-logger.ts new file mode 100644 index 00000000..6eff4277 --- /dev/null +++ b/src/util/frontend-logger.ts @@ -0,0 +1,101 @@ +// utils/frontend-logger.ts +import winston from "winston"; +import DailyRotateFile from "winston-daily-rotate-file"; +import path from "path"; + +// Define log levels and their priorities +const levels = { + error: 0, + warn: 1, + info: 2, + debug: 3, +}; + +// Define interface for log entries +export interface LogEntry { + level: keyof typeof levels; + message: string; + data?: any; + timestamp?: string; + userAgent?: string; + ip?: string; + url?: string; +} + +// Custom format for log entries +const logFormat = winston.format.combine( + winston.format.timestamp({ + format: "YYYY-MM-DD HH:mm:ss", + }), + winston.format.metadata({ fillExcept: ["message", "level", "timestamp"] }), + winston.format.json() +); + +// Create the logger instance +const frontendLogger: winston.Logger = winston.createLogger({ + levels, + level: process.env.LOG_LEVEL || "info", + format: logFormat, + transports: [ + // Daily Rotate File for errors + new DailyRotateFile({ + filename: path.join(process.cwd(), "logs/frontend/error-%DATE%.log"), + datePattern: "YYYY-MM-DD", + zippedArchive: true, + maxSize: "20m", + maxFiles: "14d", + level: "error", + format: logFormat, + }), + + // Daily Rotate File for all logs + new DailyRotateFile({ + filename: path.join(process.cwd(), "logs/frontend/combined-%DATE%.log"), + datePattern: "YYYY-MM-DD", + zippedArchive: true, + maxSize: "20m", + maxFiles: "14d", + format: logFormat, + }), + ], + // Handle errors from the logger itself + exitOnError: false, +}); + +// Add console transport in development +if (process.env.NODE_ENV !== "production") { + frontendLogger.add( + new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.simple() + ), + }) + ); +} + +// Helper functions for type-safe logging +export function logError(message: string, data?: any) { + frontendLogger.error(message, { data }); +} + +export function logWarn(message: string, data?: any) { + frontendLogger.warn(message, { data }); +} + +export function logInfo(message: string, data?: any) { + frontendLogger.info(message, { data }); +} + +export function logDebug(message: string, data?: any) { + frontendLogger.debug(message, { data }); +} + +// Helper function for dynamic logging +export function log(entry: LogEntry) { + const { level, message, ...metadata } = entry; + frontendLogger[level](message, metadata); +} + +// Export the logger instance as default +export default frontendLogger; From b5375f272c35c956f5fe5db984a857885c196c31 Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 16 Dec 2024 17:40:52 +0800 Subject: [PATCH 070/595] upd : investasi portofolio Deskripsi: - api pada list investasi portofolio - semua status No Issues --- src/app/api/new/investasi/route.ts | 32 +++++++ .../investasi/main/portofolio/[id]/page.tsx | 17 ++-- .../comp_card_portofolio_not_publish_new.tsx | 44 +++++++++ .../main/comp_card_portofolio_publish_new.tsx | 89 +++++++++++++++++++ .../investasi/_lib/type_investasi.ts | 2 + src/app_modules/investasi/_ui/index.ts | 2 + .../investasi/_ui/main/ui_portofolio_new.tsx | 56 ++++++++++++ .../main/portofolio/skeleton_portofolio.tsx | 31 +++++++ .../main/portofolio/view_portofolio_new.tsx | 79 ++++++++++++++++ .../_view/main/view_portofolio_new.tsx | 0 10 files changed, 344 insertions(+), 8 deletions(-) create mode 100644 src/app_modules/investasi/_component/main/comp_card_portofolio_not_publish_new.tsx create mode 100644 src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx create mode 100644 src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx create mode 100644 src/app_modules/investasi/_view/main/portofolio/skeleton_portofolio.tsx create mode 100644 src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx create mode 100644 src/app_modules/investasi/_view/main/view_portofolio_new.tsx diff --git a/src/app/api/new/investasi/route.ts b/src/app/api/new/investasi/route.ts index a8939023..ea13150c 100644 --- a/src/app/api/new/investasi/route.ts +++ b/src/app/api/new/investasi/route.ts @@ -98,6 +98,38 @@ export async function GET(request: Request) { 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 + })) + + } diff --git a/src/app/dev/investasi/main/portofolio/[id]/page.tsx b/src/app/dev/investasi/main/portofolio/[id]/page.tsx index b8378bd8..b9307680 100644 --- a/src/app/dev/investasi/main/portofolio/[id]/page.tsx +++ b/src/app/dev/investasi/main/portofolio/[id]/page.tsx @@ -1,22 +1,23 @@ import { investasi_funGetPortofolioByStatusId } from "@/app_modules/investasi/_fun"; -import { Investasi_UiPortofolio } from "@/app_modules/investasi/_ui"; +import { Investasi_UiPortofolio, Investasi_UiPortofolioNew } from "@/app_modules/investasi/_ui"; import getStatusInvestasi from "@/app_modules/investasi/fun/master/get_status_investasi"; export default async function Page({ params }: { params: { id: string } }) { - const statusId = params.id; + // const statusId = params.id; const listStatus = await getStatusInvestasi(); - const dataPortofolio = await investasi_funGetPortofolioByStatusId({ - page: 1, - statusId: statusId, - }); + // const dataPortofolio = await investasi_funGetPortofolioByStatusId({ + // page: 1, + // statusId: statusId, + // }); return ( <> - + /> */} + ); } diff --git a/src/app_modules/investasi/_component/main/comp_card_portofolio_not_publish_new.tsx b/src/app_modules/investasi/_component/main/comp_card_portofolio_not_publish_new.tsx new file mode 100644 index 00000000..c87fc3a9 --- /dev/null +++ b/src/app_modules/investasi/_component/main/comp_card_portofolio_not_publish_new.tsx @@ -0,0 +1,44 @@ +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { ComponentGlobal_CardStyles, ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; +import { Grid, Stack, Text } from "@mantine/core"; +import _ from "lodash"; +import { useRouter } from "next/navigation"; +import { IDataInvestasiBursa } from "../../_lib/type_investasi"; + +export function Investasi_ComponentCardPortofolio_NotPublishNew({ data, }: { data: IDataInvestasiBursa; }) { + const router = useRouter(); + return ( + <> + { + router.push(NEW_RouterInvestasi.detail_portofolio({ id: data.id }), { scroll: false }); + }} + > + + + + {" "} + {_.startCase(data.title)} + + + Target Dana: + + Rp.{" "} + {new Intl.NumberFormat("id-ID", { + maximumSignificantDigits: 10, + }).format(+data.targetDana)} + + + + + + + + + + + ); +} diff --git a/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx b/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx new file mode 100644 index 00000000..8366a163 --- /dev/null +++ b/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx @@ -0,0 +1,89 @@ +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { ComponentGlobal_CardLoadingOverlay, ComponentGlobal_LoadImageCustom, } from "@/app_modules/_global/component"; +import { Box, Grid, Group, Stack, Text } from "@mantine/core"; +import { IconChecklist, IconCircleCheck } from "@tabler/icons-react"; +import _ from "lodash"; +import moment from "moment"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { IDataInvestasiBursa } from "../../_lib/type_investasi"; +import { Investasi_ComponentStylesCard } from "../comp_card_border_and_background"; + +export function Investasi_ComponentCardPortofolioPublishNew({ data }: { data: IDataInvestasiBursa; }) { + const router = useRouter(); + const [visible, setVisible] = useState(false); + + return ( + <> + { + router.push(NEW_RouterInvestasi.detail_main({ id: data?.id }), { + scroll: false, + }); + setVisible(true); + }} + > + + + {_.capitalize(data?.title)} + + Target Dana: + + Rp.{" "} + {new Intl.NumberFormat("id-ID", { + maximumSignificantDigits: 10, + }).format(+data?.targetDana)} + + + + + + + + + + + {Number(data?.pencarianInvestor) - + moment(new Date()).diff(new Date(data?.updatedAt), "days") <= + 0 ? ( + + + Selesai + + ) : ( + + + {Number(data?.pencarianInvestor) - + moment(new Date()).diff(new Date(data?.countDown), "days") <= + 0 ? ( + + + Selesai + + ) : ( + + Sisa Waktu : {} + {Number(data?.pencarianInvestor) - + moment(new Date()).diff( + new Date(data?.countDown), + "days" + )}{" "} + hari + + )} + + + )} + + {visible && } + + + ); +} diff --git a/src/app_modules/investasi/_lib/type_investasi.ts b/src/app_modules/investasi/_lib/type_investasi.ts index b794fb1b..1f8433b3 100644 --- a/src/app_modules/investasi/_lib/type_investasi.ts +++ b/src/app_modules/investasi/_lib/type_investasi.ts @@ -20,5 +20,7 @@ export interface IDataInvestasiBursa { imageId: string progress: string countDown: string + targetDana: string pencarianInvestor: string + updatedAt: Date } \ No newline at end of file diff --git a/src/app_modules/investasi/_ui/index.ts b/src/app_modules/investasi/_ui/index.ts index 6d51522c..956c4bef 100644 --- a/src/app_modules/investasi/_ui/index.ts +++ b/src/app_modules/investasi/_ui/index.ts @@ -26,6 +26,7 @@ import { Investasi_UiCreateBerita } from "./create/ui_create_berita"; import { Investasi_UiDetailBerita } from "./detail/ui_berita"; import { Investasi_UiEditInvestasiNew } from "./edit/ui_edit_investasi_new"; import { Investasi_ViewBerandaNew } from "../_view/main/view_beranda_new"; +import { Investasi_UiPortofolioNew } from "./main/ui_portofolio_new"; export { Investasi_UiProsesPembelian }; export { Investasi_UiMetodePembayaran }; @@ -55,3 +56,4 @@ export { Investasi_UiCreateBerita }; export { Investasi_UiDetailBerita }; export { Investasi_UiEditInvestasiNew } export { Investasi_ViewBerandaNew } +export { Investasi_UiPortofolioNew } diff --git a/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx b/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx new file mode 100644 index 00000000..e21c6d28 --- /dev/null +++ b/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx @@ -0,0 +1,56 @@ +"use client"; +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import { MODEL_NEW_DEFAULT_MASTER } from "@/app_modules/model_global/interface"; +import { Stack, Tabs } from "@mantine/core"; +import { useParams, useRouter } from "next/navigation"; +import { Investasi_ViewPortofolioNew } from "../../_view/main/portofolio/view_portofolio_new"; + +export function Investasi_UiPortofolioNew({ listStatus }: { listStatus: MODEL_NEW_DEFAULT_MASTER[] }) { + const param = useParams<{ id: string }>(); + const router = useRouter(); + + return ( + <> + { + router.push(NEW_RouterInvestasi.portofolio({ id: val })); + }} + > + + + {listStatus.map((e) => ( + + {e.name} + + ))} + + + + + + + ); +} 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..417c0431 --- /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; + }} + > + { + param.id == "1" ? + (item) => () + : + (item) => () + } +
+ ) + } +
+ + ); +} diff --git a/src/app_modules/investasi/_view/main/view_portofolio_new.tsx b/src/app_modules/investasi/_view/main/view_portofolio_new.tsx new file mode 100644 index 00000000..e69de29b From b8fcbfb2dff80b088bcd411f95c2465bd6ba4b66 Mon Sep 17 00:00:00 2001 From: amel Date: Mon, 16 Dec 2024 17:52:31 +0800 Subject: [PATCH 071/595] upd: investasi - portofolio Deskripsi: - ketinggalan : matiin loading - hapus file yg ga jadi kepake No Issues --- .../_component/main/comp_card_portofolio_publish_new.tsx | 6 +----- .../investasi/_view/main/view_portofolio_new.tsx | 0 2 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 src/app_modules/investasi/_view/main/view_portofolio_new.tsx diff --git a/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx b/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx index 8366a163..61e0e9e5 100644 --- a/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx +++ b/src/app_modules/investasi/_component/main/comp_card_portofolio_publish_new.tsx @@ -1,17 +1,15 @@ import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; -import { ComponentGlobal_CardLoadingOverlay, ComponentGlobal_LoadImageCustom, } from "@/app_modules/_global/component"; +import { ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; import { Box, Grid, Group, Stack, Text } from "@mantine/core"; import { IconChecklist, IconCircleCheck } from "@tabler/icons-react"; import _ from "lodash"; import moment from "moment"; import { useRouter } from "next/navigation"; -import { useState } from "react"; import { IDataInvestasiBursa } from "../../_lib/type_investasi"; import { Investasi_ComponentStylesCard } from "../comp_card_border_and_background"; export function Investasi_ComponentCardPortofolioPublishNew({ data }: { data: IDataInvestasiBursa; }) { const router = useRouter(); - const [visible, setVisible] = useState(false); return ( <> @@ -21,7 +19,6 @@ export function Investasi_ComponentCardPortofolioPublishNew({ data }: { data: ID router.push(NEW_RouterInvestasi.detail_main({ id: data?.id }), { scroll: false, }); - setVisible(true); }} > @@ -82,7 +79,6 @@ export function Investasi_ComponentCardPortofolioPublishNew({ data }: { data: ID )} - {visible && } ); diff --git a/src/app_modules/investasi/_view/main/view_portofolio_new.tsx b/src/app_modules/investasi/_view/main/view_portofolio_new.tsx deleted file mode 100644 index e69de29b..00000000 From c4b2a47c42763d7327d1616fa0bd036e517b7da2 Mon Sep 17 00:00:00 2001 From: amel Date: Tue, 17 Dec 2024 10:44:14 +0800 Subject: [PATCH 072/595] upd: investasi portofolio Deskripsi: - ganti server action status portofolio menjadi data json No Issues --- .../investasi/main/portofolio/[id]/page.tsx | 8 +++--- .../investasi/_ui/main/ui_portofolio_new.tsx | 27 ++++++++++++++++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/app/dev/investasi/main/portofolio/[id]/page.tsx b/src/app/dev/investasi/main/portofolio/[id]/page.tsx index b9307680..4b135800 100644 --- a/src/app/dev/investasi/main/portofolio/[id]/page.tsx +++ b/src/app/dev/investasi/main/portofolio/[id]/page.tsx @@ -1,10 +1,8 @@ -import { investasi_funGetPortofolioByStatusId } from "@/app_modules/investasi/_fun"; -import { Investasi_UiPortofolio, Investasi_UiPortofolioNew } 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 listStatus = await getStatusInvestasi(); // const dataPortofolio = await investasi_funGetPortofolioByStatusId({ // page: 1, // statusId: statusId, @@ -17,7 +15,7 @@ export default async function Page({ params }: { params: { id: string } }) { listStatus={listStatus as any} dataPortofolio={dataPortofolio as any} /> */} - + ); } diff --git a/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx b/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx index e21c6d28..05b97ad2 100644 --- a/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx +++ b/src/app_modules/investasi/_ui/main/ui_portofolio_new.tsx @@ -1,14 +1,35 @@ "use client"; import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; import { AccentColor, MainColor } from "@/app_modules/_global/color"; -import { MODEL_NEW_DEFAULT_MASTER } from "@/app_modules/model_global/interface"; import { Stack, Tabs } from "@mantine/core"; import { useParams, useRouter } from "next/navigation"; import { Investasi_ViewPortofolioNew } from "../../_view/main/portofolio/view_portofolio_new"; -export function Investasi_UiPortofolioNew({ listStatus }: { listStatus: MODEL_NEW_DEFAULT_MASTER[] }) { +export function Investasi_UiPortofolioNew() { const param = useParams<{ id: string }>(); const router = useRouter(); + const status = [ + { + id: "1", + name: "Publish", + color: "green" + }, + { + id: "2", + name: "Review", + color: "orange" + }, + { + id: "3", + name: "Draft", + color: "yellow" + }, + { + id: "4", + name: "Reject", + color: "red" + } + ] return ( <> @@ -30,7 +51,7 @@ export function Investasi_UiPortofolioNew({ listStatus }: { listStatus: MODEL_NE > - {listStatus.map((e) => ( + {status.map((e) => ( Date: Tue, 17 Dec 2024 10:56:25 +0800 Subject: [PATCH 073/595] fix: donasi Deskripsi: - beranda = salah codingan load saat ada data baru No Issues --- src/app_modules/donasi/main/beranda_new.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app_modules/donasi/main/beranda_new.tsx b/src/app_modules/donasi/main/beranda_new.tsx index 04385702..f6d1b66c 100644 --- a/src/app_modules/donasi/main/beranda_new.tsx +++ b/src/app_modules/donasi/main/beranda_new.tsx @@ -37,7 +37,7 @@ export default function MainDonasiNew() { async function onLoadData({ onPublish }: { onPublish: (val: any) => void }) { setIsLoading(true); const loadData = await apiGetAllDonasi(`?cat=beranda&page=1`) - onPublish(loadData); + onPublish(loadData.data) setIsShowUpdate(false); setIsTriggerDonasiBeranda(false); From 11b938314163b67c4dce92cb8adce35a068d084f Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 17 Dec 2024 11:12:27 +0800 Subject: [PATCH 074/595] Logs Deskripsi: - Fitur logs untuk update foto profile - Fitu logs untuk update background profile --- src/app/dev/notifikasi/collaboration/page.tsx | 4 +- src/app/lib/prisma.ts | 22 +++--- .../button/comp_button_upload_photo.tsx | 3 +- src/app_modules/auth/register/view.tsx | 6 +- src/app_modules/auth/validasi/view.tsx | 41 ++++++----- .../comp_button_update_background_profile.tsx | 70 +++++++++++++------ .../comp_button_update_photo_profile.tsx | 62 ++++++++++------ .../profile/upload/foto_background/index.tsx | 1 + .../profile/upload/foto_profile/index.tsx | 1 + 9 files changed, 129 insertions(+), 81 deletions(-) diff --git a/src/app/dev/notifikasi/collaboration/page.tsx b/src/app/dev/notifikasi/collaboration/page.tsx index 78f26b83..edf580a3 100644 --- a/src/app/dev/notifikasi/collaboration/page.tsx +++ b/src/app/dev/notifikasi/collaboration/page.tsx @@ -1,6 +1,4 @@ -import { - Notifikasi_UiCollaboration -} from "@/app_modules/notifikasi/_ui"; +import { Notifikasi_UiCollaboration } from "@/app_modules/notifikasi/_ui"; import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; export default async function Page() { diff --git a/src/app/lib/prisma.ts b/src/app/lib/prisma.ts index 25a08ef9..07ccb3e8 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_modules/_global/button/comp_button_upload_photo.tsx b/src/app_modules/_global/button/comp_button_upload_photo.tsx index 8e56d342..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, @@ -30,7 +31,7 @@ export function ComponentGlobal_ButtonUploadFileImage({ onSetImage(buffer); } } catch (error) { - console.log(error); + clientLogger.error("Upload error:", error); } }} accept="image/png,image/jpeg" 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/katalog/profile/_component/button/comp_button_update_background_profile.tsx b/src/app_modules/katalog/profile/_component/button/comp_button_update_background_profile.tsx index 92f369cf..bb947e84 100644 --- a/src/app_modules/katalog/profile/_component/button/comp_button_update_background_profile.tsx +++ b/src/app_modules/katalog/profile/_component/button/comp_button_update_background_profile.tsx @@ -1,47 +1,71 @@ +import { DIRECTORY_ID } from "@/app/lib"; import { MainColor } from "@/app_modules/_global/color"; +import { + funGlobal_DeleteFileById, + funGlobal_UploadToStorage, +} from "@/app_modules/_global/fun"; import { ComponentGlobal_NotifikasiBerhasil, ComponentGlobal_NotifikasiGagal, ComponentGlobal_NotifikasiPeringatan, } from "@/app_modules/_global/notif_global"; +import { clientLogger } from "@/util/clientLogger"; import { Box, Button } from "@mantine/core"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { profile_funUpdateBackground } from "../../fun/update/fun_update_background"; -import { DIRECTORY_ID } from "@/app/lib"; -import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; export function Profile_ComponentButtonUpdateBackgroundProfile({ - profileId, file, + profileId, + fileId, }: { - profileId: string; file: File; + profileId: string; + fileId: string; }) { const router = useRouter(); const [loading, setLoading] = useState(false); async function onUpdate() { - setLoading(true); - const uploadFile = await funGlobal_UploadToStorage({ - file: file, - dirId: DIRECTORY_ID.profile_background, - }); - if (!uploadFile.success) { - setLoading(false); - return ComponentGlobal_NotifikasiPeringatan("Gagal upload foto profile"); - } - - const res = await profile_funUpdateBackground({ - profileId: profileId, - fileId: uploadFile.data.id, - }); - if (res.status === 200) { + try { setLoading(true); - ComponentGlobal_NotifikasiBerhasil(res.message); - router.back(); - } else { + const deletePhoto = await funGlobal_DeleteFileById({ + fileId: fileId, + dirId: DIRECTORY_ID.profile_background, + }); + + if (!deletePhoto.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal update background"); + return; + } + + const uploadFile = await funGlobal_UploadToStorage({ + file: file, + dirId: DIRECTORY_ID.profile_background, + }); + + if (!uploadFile.success) { + setLoading(false); + return ComponentGlobal_NotifikasiPeringatan( + "Gagal upload background" + ); + } + + const res = await profile_funUpdateBackground({ + profileId: profileId, + fileId: uploadFile.data.id, + }); + + if (res.status === 200) { + ComponentGlobal_NotifikasiBerhasil(res.message); + router.back(); + } else { + ComponentGlobal_NotifikasiGagal(res.message); + } + } catch (error) { + clientLogger.error("Error upload background", error); + } finally { setLoading(false); - ComponentGlobal_NotifikasiGagal(res.message); } } diff --git a/src/app_modules/katalog/profile/_component/button/comp_button_update_photo_profile.tsx b/src/app_modules/katalog/profile/_component/button/comp_button_update_photo_profile.tsx index ab09104b..ac721d40 100644 --- a/src/app_modules/katalog/profile/_component/button/comp_button_update_photo_profile.tsx +++ b/src/app_modules/katalog/profile/_component/button/comp_button_update_photo_profile.tsx @@ -10,40 +10,62 @@ import { Box, Button, Center } from "@mantine/core"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { profile_funUpdatePhoto } from "../../fun"; -import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; +import { + funGlobal_DeleteFileById, + funGlobal_UploadToStorage, +} from "@/app_modules/_global/fun"; import { DIRECTORY_ID } from "@/app/lib"; +import { clientLogger } from "@/util/clientLogger"; export function Profile_ComponentButtonUpdatePhotoProfile({ file, profileId, + fileId, }: { file: File; profileId: string; + fileId: string; }) { const router = useRouter(); const [isLoading, setLoading] = useState(false); async function onUpdate() { - setLoading(true); - const uploadPhoto = await funGlobal_UploadToStorage({ - file: file, - dirId: DIRECTORY_ID.profile_foto, - }); - if (!uploadPhoto.success) { - setLoading(false); - return ComponentGlobal_NotifikasiPeringatan("Gagal upload foto profile"); - } - - const res = await profile_funUpdatePhoto({ - fileId: uploadPhoto.data.id, - profileId: profileId, - }); - if (res.status === 200) { + try { setLoading(true); - ComponentGlobal_NotifikasiBerhasil(res.message); - router.back(); - } else { + const deletePhoto = await funGlobal_DeleteFileById({ + fileId: fileId, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (!deletePhoto.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal update foto profile"); + return; + } + + const uploadPhoto = await funGlobal_UploadToStorage({ + file: file, + dirId: DIRECTORY_ID.profile_foto, + }); + + if (!uploadPhoto.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal upload foto profile"); + return; + } + + const res = await profile_funUpdatePhoto({ + fileId: uploadPhoto.data.id, + profileId: profileId, + }); + + if (res.status === 200) { + ComponentGlobal_NotifikasiBerhasil(res.message); + router.back(); + } else { + ComponentGlobal_NotifikasiGagal(res.message); + } + } catch (error) { + clientLogger.error("Error update photo profile", error); + } finally { setLoading(false); - ComponentGlobal_NotifikasiGagal(res.message); } } return ( diff --git a/src/app_modules/katalog/profile/upload/foto_background/index.tsx b/src/app_modules/katalog/profile/upload/foto_background/index.tsx index 559b003c..4772c7ed 100644 --- a/src/app_modules/katalog/profile/upload/foto_background/index.tsx +++ b/src/app_modules/katalog/profile/upload/foto_background/index.tsx @@ -48,6 +48,7 @@ export default function Profile_UpdateFotoBackground({ diff --git a/src/app_modules/katalog/profile/upload/foto_profile/index.tsx b/src/app_modules/katalog/profile/upload/foto_profile/index.tsx index 4f1a960e..7eec7bdf 100644 --- a/src/app_modules/katalog/profile/upload/foto_profile/index.tsx +++ b/src/app_modules/katalog/profile/upload/foto_profile/index.tsx @@ -44,6 +44,7 @@ export default function UploadFotoProfile({
From c08d72539b1084a5318c1217bb37c891e0a93851 Mon Sep 17 00:00:00 2001 From: amel Date: Tue, 17 Dec 2024 11:56:58 +0800 Subject: [PATCH 075/595] upd : donasi Deskripsi: - update api list donasi galang data semua status - fix code more data No Issues --- src/app/api/new/donasi/route.ts | 25 +++++- .../dev/donasi/main/galang_dana/[id]/page.tsx | 24 +++--- .../component/card_view/card_publish_new.tsx | 2 - .../component/card_view/card_status_new.tsx | 53 ++++++++++++ src/app_modules/donasi/lib/type_donasi.ts | 1 + src/app_modules/donasi/main/beranda_new.tsx | 2 +- .../galang_dana/donasi_galang_dana_new.tsx | 80 +++++++++++++++++++ .../main/galang_dana/ui_galang_dana_new.tsx | 33 +++++--- .../main/portofolio/view_portofolio_new.tsx | 2 +- 9 files changed, 191 insertions(+), 31 deletions(-) create mode 100644 src/app_modules/donasi/component/card_view/card_status_new.tsx create mode 100644 src/app_modules/donasi/main/galang_dana/donasi_galang_dana_new.tsx 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/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_modules/donasi/component/card_view/card_publish_new.tsx b/src/app_modules/donasi/component/card_view/card_publish_new.tsx index ab84a3a2..885fa359 100644 --- a/src/app_modules/donasi/component/card_view/card_publish_new.tsx +++ b/src/app_modules/donasi/component/card_view/card_publish_new.tsx @@ -13,8 +13,6 @@ export default function ComponentDonasi_CardPublishNew({ data }: { data: any; }) <> { - // DELSOON - console.log(RouterDonasi.detail_main + `${data.id}`) router.push(RouterDonasi.detail_main + `${data.id}`) }} > diff --git a/src/app_modules/donasi/component/card_view/card_status_new.tsx b/src/app_modules/donasi/component/card_view/card_status_new.tsx new file mode 100644 index 00000000..0d2d888e --- /dev/null +++ b/src/app_modules/donasi/component/card_view/card_status_new.tsx @@ -0,0 +1,53 @@ +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { ComponentGlobal_CardStyles, ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; +import { Grid, Stack, Text } from "@mantine/core"; +import { useParams, useRouter } from "next/navigation"; +import { IDataAllDonasi } from "../../lib/type_donasi"; + +export function ComponentDonasi_CardStatusNew({ data }: { data: IDataAllDonasi; }) { + const router = useRouter(); + const param = useParams<{ id: string }>(); + + function goToDetail() { + if (param.id == "2") { + router.push(RouterDonasi.detail_review + `${data.id}`); + } else if (param.id == "3") { + router.push(RouterDonasi.detail_draft + `${data.id}`); + } else if (param.id == "4") { + router.push(RouterDonasi.detail_reject + `${data.id}`); + } + } + + return ( + <> + { goToDetail() }} > + + + + + + + + + {data.title} + + + Target Dana + + Rp.{" "} + {new Intl.NumberFormat("id-ID", { + maximumFractionDigits: 10, + }).format(+data.target)} + + + + + + + + + ); +} diff --git a/src/app_modules/donasi/lib/type_donasi.ts b/src/app_modules/donasi/lib/type_donasi.ts index fabb23d5..2931b59f 100644 --- a/src/app_modules/donasi/lib/type_donasi.ts +++ b/src/app_modules/donasi/lib/type_donasi.ts @@ -5,5 +5,6 @@ export interface IDataAllDonasi { publishTime: Date progres: string terkumpul: string + target: string nameDonasiDurasi: string } \ No newline at end of file diff --git a/src/app_modules/donasi/main/beranda_new.tsx b/src/app_modules/donasi/main/beranda_new.tsx index f6d1b66c..a8c4dde1 100644 --- a/src/app_modules/donasi/main/beranda_new.tsx +++ b/src/app_modules/donasi/main/beranda_new.tsx @@ -117,7 +117,7 @@ export default function MainDonasiNew() { setActivePage((val) => val + 1); - return loadData; + return loadData.data; }} > {(item) => ( diff --git a/src/app_modules/donasi/main/galang_dana/donasi_galang_dana_new.tsx b/src/app_modules/donasi/main/galang_dana/donasi_galang_dana_new.tsx new file mode 100644 index 00000000..d4d81c42 --- /dev/null +++ b/src/app_modules/donasi/main/galang_dana/donasi_galang_dana_new.tsx @@ -0,0 +1,80 @@ +"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 { useParams } from "next/navigation"; +import { useState } from "react"; +import ComponentDonasi_CardPublishNew from "../../component/card_view/card_publish_new"; +import SkeletonDonasi from "../../component/skeleton_donasi"; +import { apiGetAllDonasi } from "../../lib/api_donasi"; +import { IDataAllDonasi } from "../../lib/type_donasi"; +import { ComponentDonasi_CardStatusNew } from "../../component/card_view/card_status_new"; + +export default function Donasi_ViewGalangDanaNew() { + const param = useParams<{ id: string }>();; + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true) + + async function getDataGalangDana() { + try { + setLoading(true) + const response = await apiGetAllDonasi(`?cat=galang-dana&status=${param.id}&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataGalangDana() + }, []); + + return ( + <> + { + loading ? + : + _.isEmpty(data) ? ( + + ) : ( + + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllDonasi(`?cat=galang-dana&status=${param.id}&page=${pageNew}`) + setActivePage((val) => val + 1); + + return loadData.data; + }} + > + { + param.id == "1" ? + (item) => () + : + (item) => () + + } +
+
+ ) + } + + ); +} diff --git a/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx b/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx index dea0613a..1aa553df 100644 --- a/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx +++ b/src/app_modules/donasi/main/galang_dana/ui_galang_dana_new.tsx @@ -1,13 +1,31 @@ "use client"; import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; import { AccentColor, MainColor, } from "@/app_modules/_global/color/color_pallet"; -import { MODEL_NEW_DEFAULT_MASTER } from "@/app_modules/model_global/interface"; import { Stack, Tabs } from "@mantine/core"; import { useParams, useRouter } from "next/navigation"; +import Donasi_ViewGalangDanaNew from "./donasi_galang_dana_new"; -export default function GalangDanaDonasiNew({listStatus,}: {listStatus: MODEL_NEW_DEFAULT_MASTER[]; }) { +export default function GalangDanaDonasiNew() { const router = useRouter(); const param = useParams<{ id: string }>(); + const status = [ + { + id: "1", + name: "Publish" + }, + { + id: "2", + name: "Review" + }, + { + id: "3", + name: "Draft" + }, + { + id: "4", + name: "Reject" + } + ] async function onChangeStatus({ statusId }: { statusId: string }) { router.replace(RouterDonasi.status_galang_dana({ id: statusId })); @@ -34,7 +52,7 @@ export default function GalangDanaDonasiNew({listStatus,}: {listStatus: MODEL_NE > - {listStatus.map((e, i) => ( + {status.map((e, i) => ( ))} - - {/* {param.id == "1" && } - - {param.id == "2" && } - - {param.id == "3" && } - - {param.id == "4" && } */} + 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 index 417c0431..5c0a76c0 100644 --- a/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx +++ b/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx @@ -61,7 +61,7 @@ export function Investasi_ViewPortofolioNew() { const loadData = await apiGetAllInvestasi(`?cat=portofolio&status=${param.id}&page=${pageNew}`) setActivePage((val) => val + 1); - return loadData; + return loadData.data; }} > { From 1caa6d55631e428d53ae1eadff70a7eef57a5b38 Mon Sep 17 00:00:00 2001 From: amel Date: Tue, 17 Dec 2024 17:12:32 +0800 Subject: [PATCH 076/595] upd: donasi Deskripsi: - api donasi saya No Issues --- src/app/api/new/donasi/invoice/route.ts | 77 +++++++++++++++++ src/app/dev/donasi/main/donasi_saya/page.tsx | 12 ++- .../component/card_view/card_invoice_new.tsx | 83 +++++++++++++++++++ .../donasi/component/skeleton_donasi.tsx | 2 +- .../donasi/component/skeleton_donasi_saya.tsx | 32 +++++++ src/app_modules/donasi/lib/api_donasi.ts | 5 ++ src/app_modules/donasi/lib/type_donasi.ts | 13 +++ .../donasi/main/donasi_saya_new.tsx | 72 ++++++++++++++++ 8 files changed, 291 insertions(+), 5 deletions(-) create mode 100644 src/app/api/new/donasi/invoice/route.ts create mode 100644 src/app_modules/donasi/component/card_view/card_invoice_new.tsx create mode 100644 src/app_modules/donasi/component/skeleton_donasi_saya.tsx create mode 100644 src/app_modules/donasi/main/donasi_saya_new.tsx 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/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_modules/donasi/component/card_view/card_invoice_new.tsx b/src/app_modules/donasi/component/card_view/card_invoice_new.tsx new file mode 100644 index 00000000..c5712648 --- /dev/null +++ b/src/app_modules/donasi/component/card_view/card_invoice_new.tsx @@ -0,0 +1,83 @@ +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { AccentColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_LoadImageCustom } from "@/app_modules/_global/component"; +import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; +import { Badge, Card, Grid, Group, Progress, Stack, Text } from "@mantine/core"; +import { useRouter } from "next/navigation"; +import { IDataAllDonasiSaya } from "../../lib/type_donasi"; +import ComponentDonasi_TampilanHitungMundur from "../tampilan_hitung_mundur"; +import TampilanRupiahDonasi from "../tampilan_rupiah"; + +export function ComponentDonasi_CardInvoiceNew({ data, }: { data: IDataAllDonasiSaya; }) { + const router = useRouter(); + + async function onCekInvoice() { + if (data.donasiMaster_StatusInvoiceId === "1") { + return router.push(RouterDonasi.detail_donasi_saya + `${data?.id}`); + } else { + if (data.donasiMaster_StatusInvoiceId === "2") { + return router.push(RouterDonasi.proses_transaksi + `${data?.id}`); + } else { + if (data.donasiMaster_StatusInvoiceId === "3") { + return router.push(RouterDonasi.invoice + `${data?.id}`); + } else { + ComponentGlobal_NotifikasiGagal("Gagal Melihat Invoice"); + } + } + } + } + + return ( + <> + onCekInvoice()} + > + + + + + + + {data.title} + + + + + + + Donasi Saya + + + + + + + {data.nameStatusInvoice} + + + + + + + + + + + ); +} diff --git a/src/app_modules/donasi/component/skeleton_donasi.tsx b/src/app_modules/donasi/component/skeleton_donasi.tsx index 7c27f924..7f461e64 100644 --- a/src/app_modules/donasi/component/skeleton_donasi.tsx +++ b/src/app_modules/donasi/component/skeleton_donasi.tsx @@ -1,5 +1,5 @@ import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; -import { Box, Grid, Group, Skeleton, Stack } from "@mantine/core"; +import { Box, Grid, Skeleton } from "@mantine/core"; export default function SkeletonDonasi() { return <> diff --git a/src/app_modules/donasi/component/skeleton_donasi_saya.tsx b/src/app_modules/donasi/component/skeleton_donasi_saya.tsx new file mode 100644 index 00000000..1b3b3a2b --- /dev/null +++ b/src/app_modules/donasi/component/skeleton_donasi_saya.tsx @@ -0,0 +1,32 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Box, Grid, Skeleton } from "@mantine/core"; + +export default function SkeletonDonasiSaya() { + return <> + + {[...Array(3)].map((_, index) => ( + + + + + {[...Array(4)].map((_, index) => ( + + + + + + + + ))} + + + + + + + + + ))} + + ; +} \ No newline at end of file diff --git a/src/app_modules/donasi/lib/api_donasi.ts b/src/app_modules/donasi/lib/api_donasi.ts index 04f661f3..2f7e719e 100644 --- a/src/app_modules/donasi/lib/api_donasi.ts +++ b/src/app_modules/donasi/lib/api_donasi.ts @@ -6,4 +6,9 @@ export const apiGetAllDonasi = async (path?: string) => { export const apiGetMasterDonasi = async (path?: string) => { const response = await fetch(`/api/new/donasi/master${(path) ? path : ''}`) return await response.json().catch(() => null) +} + +export const apiGetAllDonasiSaya = async (path?: string) => { + const response = await fetch(`/api/new/donasi/invoice${(path) ? path : ''}`) + return await response.json().catch(() => null) } \ No newline at end of file diff --git a/src/app_modules/donasi/lib/type_donasi.ts b/src/app_modules/donasi/lib/type_donasi.ts index 2931b59f..912d067f 100644 --- a/src/app_modules/donasi/lib/type_donasi.ts +++ b/src/app_modules/donasi/lib/type_donasi.ts @@ -7,4 +7,17 @@ export interface IDataAllDonasi { terkumpul: string target: string nameDonasiDurasi: string +} + +export interface IDataAllDonasiSaya { + id: string + nominal: string + donasiMaster_StatusInvoiceId: string + nameStatusInvoice: string + donasiId: string + title: string + publishTime: Date + progres: string + imageId: string + durasiDonasi: string } \ No newline at end of file diff --git a/src/app_modules/donasi/main/donasi_saya_new.tsx b/src/app_modules/donasi/main/donasi_saya_new.tsx new file mode 100644 index 00000000..0ecf12b1 --- /dev/null +++ b/src/app_modules/donasi/main/donasi_saya_new.tsx @@ -0,0 +1,72 @@ +"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 { ComponentDonasi_CardInvoiceNew } from "../component/card_view/card_invoice_new"; +import SkeletonDonasiSaya from "../component/skeleton_donasi_saya"; +import { apiGetAllDonasiSaya } from "../lib/api_donasi"; +import { IDataAllDonasiSaya } from "../lib/type_donasi"; + +export default function DonasiSayaNew() { + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true) + + async function getDataDonasiSaya() { + try { + setLoading(true) + const response = await apiGetAllDonasiSaya(`?page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataDonasiSaya() + }, []); + + return ( + <> + + { + loading ? + + : + _.isEmpty(data) ? ( + + ) : ( + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllDonasiSaya(`?page=${pageNew}`) + setActivePage((val) => val + 1); + + return loadData.data; + }} + > + {(item) => } +
+ ) + } +
+ + ); +} \ No newline at end of file From 2fa3f22f9c907de560f4b4e865a6964582a6df16 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 17 Dec 2024 22:40:38 +0800 Subject: [PATCH 077/595] Fix notifikasi Deskripsi: - Ganti get notifikasi dengan API --- .../notifikasi/get-all-by-category/route.ts | 63 ++++++++++++++++ src/app/dev/notifikasi/event/page.tsx | 8 +-- src/app/dev/notifikasi/semua/page.tsx | 8 +-- .../api_user_router/route_api_notifikasi.ts | 11 +++ src/app_modules/home/view_home_new.tsx | 4 ++ .../notifikasi/_ui/ui_all_notifikasi.tsx | 72 +++++++++++-------- .../notifikasi/_ui/ui_event_notifikasi.tsx | 51 +++++++------ .../_ui/ui_new_layout_notifikasi.tsx | 17 ++--- .../notifikasi/component/card_view.tsx | 17 ++--- src/app_modules/notifikasi/component/index.ts | 2 + .../notifikasi/component/skeleton_view.tsx | 29 ++++++++ src/app_modules/notifikasi/model/interface.ts | 12 +++- src/middleware.ts | 3 +- 13 files changed, 208 insertions(+), 89 deletions(-) create mode 100644 src/app/api/notifikasi/get-all-by-category/route.ts create mode 100644 src/app/lib/api_user_router/route_api_notifikasi.ts create mode 100644 src/app_modules/notifikasi/component/skeleton_view.tsx 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..b13ce671 --- /dev/null +++ b/src/app/api/notifikasi/get-all-by-category/route.ts @@ -0,0 +1,63 @@ +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) { + if (request.method === "GET") { + try { + 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") { + const data = await prisma.notifikasi.findMany({ + take: takeData, + skip: skipData, + orderBy: [ + { + isRead: "asc", + }, + { createdAt: "desc" }, + ], + where: { + userId: userLoginId, + userRoleId: "1", + }, + }); + + return NextResponse.json({ success: true, data }); + } + + const allData = 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: allData }); + } catch (error) { + backendLogger.error("Error get data notifikasi: " + error); + } + } else { + return NextResponse.json({ success: false, message: "Method not allowed" }); + } +} diff --git a/src/app/dev/notifikasi/event/page.tsx b/src/app/dev/notifikasi/event/page.tsx index fa2ce235..3b7b8e34 100644 --- a/src/app/dev/notifikasi/event/page.tsx +++ b/src/app/dev/notifikasi/event/page.tsx @@ -1,15 +1,11 @@ import { Notifikasi_UiEvent } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Event", - }); + // return ( <> - + ); } diff --git a/src/app/dev/notifikasi/semua/page.tsx b/src/app/dev/notifikasi/semua/page.tsx index f9fad445..2f691b3c 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"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Semua", - }); - 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..94027735 --- /dev/null +++ b/src/app/lib/api_user_router/route_api_notifikasi.ts @@ -0,0 +1,11 @@ +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}`, +}; diff --git a/src/app_modules/home/view_home_new.tsx b/src/app_modules/home/view_home_new.tsx index de88ed02..59bca9fc 100644 --- a/src/app_modules/home/view_home_new.tsx +++ b/src/app_modules/home/view_home_new.tsx @@ -17,6 +17,7 @@ import BodyHome from "./component/body_home"; import FooterHome from "./component/footer_home"; import { apiGetDataHome } from "./fun/get/api_home"; import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; +import { gs_notifikasi_kategori_app } from "../notifikasi/lib"; export default function HomeViewNew({ countNotifikasi, @@ -27,6 +28,7 @@ export default function HomeViewNew({ const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); const [dataUser, setDataUser] = useState({}); + const [categoryPage, setCategoryPage] = useAtom(gs_notifikasi_kategori_app); const router = useRouter(); useShallowEffect(() => { @@ -98,12 +100,14 @@ export default function HomeViewNew({ ) { router.push(RouterProfile.create, { scroll: false }); } else { + setCategoryPage("Semua") router.push( RouterNotifikasi.categoryApp({ name: "semua" }), { scroll: false, } ); + } }} > diff --git a/src/app_modules/notifikasi/_ui/ui_all_notifikasi.tsx b/src/app_modules/notifikasi/_ui/ui_all_notifikasi.tsx index 02553505..7d3fa08f 100644 --- a/src/app_modules/notifikasi/_ui/ui_all_notifikasi.tsx +++ b/src/app_modules/notifikasi/_ui/ui_all_notifikasi.tsx @@ -2,46 +2,57 @@ 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 { + Box, + Center, + Divider, + Grid, + Group, + Loader, + Skeleton, + Stack, +} from "@mantine/core"; import _ from "lodash"; import { ScrollOnly } from "next-scroll-loader"; import { useState } from "react"; -import { ComponentNotifiaksi_CardView } from "../component"; +import { + ComponentNotifiaksi_CardView, + Notifikasi_ComponentSkeletonView, +} from "../component"; import notifikasi_getByUserId from "../fun/get/get_notifiaksi_by_id"; -import { MODEL_NOTIFIKASI } from "../model/interface"; +import { ICategoryapp, MODEL_NOTIFIKASI } from "../model/interface"; import { useAtom } from "jotai"; import { gs_notifikasi_kategori_app } from "../lib"; import { useShallowEffect } from "@mantine/hooks"; +import { API_RouteNotifikasi } from "@/app/lib/api_user_router/route_api_notifikasi"; +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; -export default function Notifikasi_UiAll({ - listNotifikasi, -}: { - listNotifikasi: any[]; -}) { - const [data, setData] = useState(listNotifikasi); +export default function Notifikasi_UiAll() { + const [data, setData] = useState(null); const [activePage, setActivePage] = useState(1); const [categoryPage, setCategoryPage] = useAtom(gs_notifikasi_kategori_app); useShallowEffect(() => { - onLoadData({ - onSetData(val: any) { - setData(val); - }, - }); - }, [setData]); + onLoadData(); + }, []); - async function onLoadData({ onSetData }: { onSetData: any }) { - const loadData = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Semua", - }); - onSetData(loadData); + async function onLoadData() { + const loadData = await fetch( + API_RouteNotifikasi.get_all_by_category({ + category: categoryPage as any, + page: 1, + }) + ); + const data = await loadData.json().then((res) => res.data as any); + setData(data); } return ( <> - {_.isEmpty(data) ? ( + {_.isNull(data) ? ( + + ) : _.isEmpty(data) ? ( ) : ( )} data={data} - setData={setData} + setData={setData as any} moreData={async () => { - const loadData = await notifikasi_getByUserId({ - page: activePage + 1, - kategoriApp: categoryPage as any, - }); + const loadData = await fetch( + API_RouteNotifikasi.get_all_by_category({ + category: categoryPage as any, + page: activePage + 1, + }) + ); + const data = await loadData.json().then((res) => res.data as any); setActivePage((val) => val + 1); - return loadData; + return data; }} > {(item) => ( )} diff --git a/src/app_modules/notifikasi/_ui/ui_event_notifikasi.tsx b/src/app_modules/notifikasi/_ui/ui_event_notifikasi.tsx index d823a46e..b88c9cb7 100644 --- a/src/app_modules/notifikasi/_ui/ui_event_notifikasi.tsx +++ b/src/app_modules/notifikasi/_ui/ui_event_notifikasi.tsx @@ -7,37 +7,39 @@ import { useAtom } from "jotai"; import _ from "lodash"; import { ScrollOnly } from "next-scroll-loader"; import { useState } from "react"; -import { ComponentNotifiaksi_CardView } from "../component"; +import { + ComponentNotifiaksi_CardView, + Notifikasi_ComponentSkeletonView, +} from "../component"; import notifikasi_getByUserId from "../fun/get/get_notifiaksi_by_id"; import { gs_notifikasi_kategori_app } from "../lib"; import { MODEL_NOTIFIKASI } from "../model/interface"; import { useShallowEffect } from "@mantine/hooks"; +import { API_RouteNotifikasi } from "@/app/lib/api_user_router/route_api_notifikasi"; -export default function Notifikasi_UiEvent({ - listNotifikasi, -}: { - listNotifikasi: any[]; -}) { - const [data, setData] = useState(listNotifikasi); +export default function Notifikasi_UiEvent() { + const [data, setData] = useState(null); const [activePage, setActivePage] = useState(1); const [categoryPage, setCategoryPage] = useAtom(gs_notifikasi_kategori_app); useShallowEffect(() => { - onLoadData(setData); - }, [setData]); + onLoadData(); + }, []); - async function onLoadData(setData: any) { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Event", - }); - setData(listNotifikasi); + async function onLoadData() { + const loadData = await fetch( + API_RouteNotifikasi.get_all_by_category({ category: categoryPage as any, page: 1 }) + ); + const data = await loadData.json().then((res) => res.data as any); + setData(data); } return ( <> - {_.isEmpty(data) ? ( + {_.isNull(data) ? ( + + ) : _.isEmpty(data) ? ( ) : ( )} data={data} - setData={setData} + setData={setData as any} moreData={async () => { - const loadData = await notifikasi_getByUserId({ - page: activePage + 1, - kategoriApp: categoryPage as any, - }); + const loadData = await fetch( + API_RouteNotifikasi.get_all_by_category({ + category: categoryPage as any, + page: activePage + 1, + }) + ); + const data = await loadData.json().then((res) => res.data as any); setActivePage((val) => val + 1); - return loadData; + return data; }} > {(item) => ( )} diff --git a/src/app_modules/notifikasi/_ui/ui_new_layout_notifikasi.tsx b/src/app_modules/notifikasi/_ui/ui_new_layout_notifikasi.tsx index 064e9f55..81b16b89 100644 --- a/src/app_modules/notifikasi/_ui/ui_new_layout_notifikasi.tsx +++ b/src/app_modules/notifikasi/_ui/ui_new_layout_notifikasi.tsx @@ -3,12 +3,12 @@ import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi"; import { MainColor } from "@/app_modules/_global/color/color_pallet"; import { - BackgroundImage, - Box, - Button, - Container, - Flex, - rem, + BackgroundImage, + Box, + Button, + Container, + Flex, + rem, } from "@mantine/core"; import { useAtom } from "jotai"; import _ from "lodash"; @@ -108,9 +108,7 @@ function UIChildren({ style={{ transition: "0.3s", backgroundColor: - categoryPage === e.name - ? MainColor.yellow - : "GrayText", + categoryPage === e.name ? MainColor.yellow : "GrayText", }} onClick={() => { router.replace( @@ -120,7 +118,6 @@ function UIChildren({ } ); setCategoryPage(e.name); - // onLoadDataNotifikasi(e.name); }} > {e.name} diff --git a/src/app_modules/notifikasi/component/card_view.tsx b/src/app_modules/notifikasi/component/card_view.tsx index d3f2b07f..c6171e9a 100644 --- a/src/app_modules/notifikasi/component/card_view.tsx +++ b/src/app_modules/notifikasi/component/card_view.tsx @@ -13,13 +13,15 @@ import { IconCheck, IconChecks } from "@tabler/icons-react"; import { useAtom } from "jotai"; import { useRouter } from "next/navigation"; import { useState } from "react"; -import { MODEL_NOTIFIKASI } from "../model/interface"; +import { ICategoryapp, MODEL_NOTIFIKASI } from "../model/interface"; import { notifikasi_eventCheckStatus } from "./path/event"; import { notifikasi_jobCheckStatus } from "./path/job"; import { gs_vote_hotMenu } from "@/app_modules/vote/global_state"; import { notifikasi_votingCheckStatus } from "./path/voting"; import { redirectDonasiPage } from "./path/donasi"; import { gs_donasi_hot_menu } from "@/app_modules/donasi/global_state"; +import moment from "moment"; +import "moment/locale/id"; export function ComponentNotifiaksi_CardView({ data, @@ -28,7 +30,7 @@ export function ComponentNotifiaksi_CardView({ }: { data: MODEL_NOTIFIKASI; onLoadData: (val: any) => void; - categoryPage: any; + categoryPage: string; }) { const router = useRouter(); const [visible, setVisible] = useState(false); @@ -220,16 +222,7 @@ export function ComponentNotifiaksi_CardView({ - {new Intl.DateTimeFormat("id-ID", { - dateStyle: "long", - }).format(data?.createdAt)} - - - {", "} - {new Intl.DateTimeFormat("id-ID", { - timeStyle: "short", - }).format(data?.createdAt)} - + {moment(data.createdAt).format("LLL")} {data?.isRead ? ( diff --git a/src/app_modules/notifikasi/component/index.ts b/src/app_modules/notifikasi/component/index.ts index cf740b8c..9d4b1d74 100644 --- a/src/app_modules/notifikasi/component/index.ts +++ b/src/app_modules/notifikasi/component/index.ts @@ -1,5 +1,7 @@ import { ComponentNotifikasi_CardSkeleton } from "./card_skeleton"; import { ComponentNotifiaksi_CardView } from "./card_view"; +import Notifikasi_ComponentSkeletonView from "./skeleton_view"; export { ComponentNotifiaksi_CardView }; export { ComponentNotifikasi_CardSkeleton }; +export { Notifikasi_ComponentSkeletonView }; diff --git a/src/app_modules/notifikasi/component/skeleton_view.tsx b/src/app_modules/notifikasi/component/skeleton_view.tsx new file mode 100644 index 00000000..c5d8f302 --- /dev/null +++ b/src/app_modules/notifikasi/component/skeleton_view.tsx @@ -0,0 +1,29 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Stack, Group, Skeleton, Divider } from "@mantine/core"; + +export default function Notifikasi_ComponentSkeletonView() { + return ( + <> + {Array.from({ length: 10 }).map((_, i) => ( + + + + + + + + + + + + + + + + + + + ))} + + ); +} diff --git a/src/app_modules/notifikasi/model/interface.ts b/src/app_modules/notifikasi/model/interface.ts index 8697bfd0..fa526102 100644 --- a/src/app_modules/notifikasi/model/interface.ts +++ b/src/app_modules/notifikasi/model/interface.ts @@ -33,8 +33,18 @@ export interface MODEL_NOTIFIKASI { | "Gagal" | "Donatur Baru" | "Kabar Donasi" - | "Pencairan Dana" + | "Pencairan Dana"; Role: MODEL_NEW_DEFAULT_MASTER; userRoleId: String; } + +export type ICategoryapp = + | "Semua" + | "Event" + | "Job" + | "Voting" + | "Donasi" + | "Investasi" + | "Forum" + | "Collaboration"; diff --git a/src/middleware.ts b/src/middleware.ts index 6a72d076..2c3e8275 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -19,6 +19,7 @@ const middlewareConfig: MiddlewareConfig = { userPath: "/dev/home", publicRoutes: [ "/", + "/api/notifikasi/*", "/api/logs/*", "/api/image/*", "/api/job/*", @@ -35,7 +36,7 @@ const middlewareConfig: MiddlewareConfig = { "/auth/api/login", "/aset/global/main_background.png", "/aset/logo/logo-hipmi.png", - "/api/new/*" + "/api/new/*", ], encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, sessionKey: process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, From fe76a695323dc0632abc9c1465f1bbd324ec7fa4 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 17 Dec 2024 22:41:47 +0800 Subject: [PATCH 078/595] chore(release): 1.2.31 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56cbce3a..dc295cf0 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.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) diff --git a/package.json b/package.json index 5091e4d2..5d5a2564 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.30", + "version": "1.2.31", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 8c9a885f4325d206eb0fe6eb544f85896542467a Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 17 Dec 2024 22:43:59 +0800 Subject: [PATCH 079/595] Fix version 1.2.31 --- src/app/lib/prisma.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/lib/prisma.ts b/src/app/lib/prisma.ts index 07ccb3e8..101fd93b 100644 --- a/src/app/lib/prisma.ts +++ b/src/app/lib/prisma.ts @@ -25,6 +25,6 @@ process.on("SIGINT", async () => { process.exit(0); }); -console.log("==> Test prisma"); +// console.log("==> Test prisma"); export default prisma; From c1931cf614179c15bea39f21aae35b6ff7f831fd Mon Sep 17 00:00:00 2001 From: amel Date: Wed, 18 Dec 2024 15:17:14 +0800 Subject: [PATCH 080/595] upd: investasi Deskripsi: - update api saham saya - update api transaksi investasi No Issues --- src/app/api/new/investasi/invoice/route.ts | 99 +++++++++++++++++++ .../dev/investasi/main/saham_saya/page.tsx | 8 +- src/app/dev/investasi/main/transaksi/page.tsx | 4 +- .../main/comp_card_daftar_transaksi_new.tsx | 94 ++++++++++++++++++ .../main/comp_card_saham_saya_new.tsx | 66 +++++++++++++ .../investasi/_lib/api_interface.ts | 5 + .../investasi/_lib/type_investasi.ts | 11 +++ .../_view/main/skeleton_saham_saya.tsx | 18 ++++ .../_view/main/view_saham_saya_new.tsx | 73 ++++++++++++++ .../_view/main/view_transaksi_new.tsx | 72 ++++++++++++++ 10 files changed, 445 insertions(+), 5 deletions(-) create mode 100644 src/app/api/new/investasi/invoice/route.ts create mode 100644 src/app_modules/investasi/_component/main/comp_card_daftar_transaksi_new.tsx create mode 100644 src/app_modules/investasi/_component/main/comp_card_saham_saya_new.tsx create mode 100644 src/app_modules/investasi/_view/main/skeleton_saham_saya.tsx create mode 100644 src/app_modules/investasi/_view/main/view_saham_saya_new.tsx create mode 100644 src/app_modules/investasi/_view/main/view_transaksi_new.tsx 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/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_modules/investasi/_component/main/comp_card_daftar_transaksi_new.tsx b/src/app_modules/investasi/_component/main/comp_card_daftar_transaksi_new.tsx new file mode 100644 index 00000000..0ada112b --- /dev/null +++ b/src/app_modules/investasi/_component/main/comp_card_daftar_transaksi_new.tsx @@ -0,0 +1,94 @@ +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { AccentColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan"; +import { Card, Group, Stack, Text, Title } from "@mantine/core"; +import { useRouter } from "next/navigation"; +import { IDataSahamSaya } from "../../_lib/type_investasi"; + +export function Investasi_ComponentCardDaftarTransaksiNew({ data }: { data: IDataSahamSaya; }) { + const router = useRouter(); + + async function onClick({ invoiceId, statusInvoiceId, }: { invoiceId: string; statusInvoiceId: string; }) { + // Berhasil + if (statusInvoiceId === "1") { + return router.push(NEW_RouterInvestasi.transaksi_berhasil + invoiceId, { + scroll: false, + }); + } + + // Proses + if (statusInvoiceId === "2") { + return router.push(NEW_RouterInvestasi.proses_transaksi + invoiceId, { + scroll: false, + }); + } + + // Menunggu + if (statusInvoiceId === "3") { + return router.push(NEW_RouterInvestasi.invoice + invoiceId, { + scroll: false, + }); + } + + if (statusInvoiceId === "4") { + return router.push(NEW_RouterInvestasi.transaksi_gagal + invoiceId, { + scroll: false, + }); + } + ComponentGlobal_NotifikasiPeringatan("Status Belum Tersedia"); + } + + + + return ( + <> + + onClick({ + invoiceId: data.id, + statusInvoiceId: data.statusInvoiceId, + }) + } + > + + {data.title} + + Rp. + {new Intl.NumberFormat("id-ID", { + maximumFractionDigits: 10, + }).format(+data.nominal)} + + + + + + {new Intl.DateTimeFormat("id-ID", { dateStyle: "long" }).format(new Date(data.createdAt))} + + + + {data.statusInvoice} + + + + + ); +} diff --git a/src/app_modules/investasi/_component/main/comp_card_saham_saya_new.tsx b/src/app_modules/investasi/_component/main/comp_card_saham_saya_new.tsx new file mode 100644 index 00000000..bfaee9d9 --- /dev/null +++ b/src/app_modules/investasi/_component/main/comp_card_saham_saya_new.tsx @@ -0,0 +1,66 @@ +import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; +import { MainColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_TampilanAngkaRatusan, ComponentGlobal_TampilanRupiah } from "@/app_modules/_global/component"; +import { Box, Progress, SimpleGrid, Stack, Text } from "@mantine/core"; +import { useRouter } from "next/navigation"; +import { IDataSahamSaya } from "../../_lib/type_investasi"; +import { Investasi_ComponentStylesCard } from "../comp_card_border_and_background"; + +export function Investasi_ComponentSahamSayaNew({ data }: { data: IDataSahamSaya; }) { + const router = useRouter(); + + return ( + <> + { + router.push(NEW_RouterInvestasi.detail_saham + data?.id, { + scroll: false, + }); + }} + marginBottom={"15px"} + > + + + + + {data?.title} + + + + + + + + + + + + + ); +} diff --git a/src/app_modules/investasi/_lib/api_interface.ts b/src/app_modules/investasi/_lib/api_interface.ts index eb9aafda..a1475cff 100644 --- a/src/app_modules/investasi/_lib/api_interface.ts +++ b/src/app_modules/investasi/_lib/api_interface.ts @@ -11,4 +11,9 @@ export const apiGetOneInvestasiById = async (path: string) => { export const apiGetAllInvestasi = async (path?: string) => { const response = await fetch(`/api/new/investasi${(path) ? path : ''}`) return await response.json().catch(() => null) +} + +export const apiGetAllSahamSaya = async (path?: string) => { + const response = await fetch(`/api/new/investasi/invoice${(path) ? path : ''}`) + return await response.json().catch(() => null) } \ No newline at end of file diff --git a/src/app_modules/investasi/_lib/type_investasi.ts b/src/app_modules/investasi/_lib/type_investasi.ts index 1f8433b3..fed5c0ad 100644 --- a/src/app_modules/investasi/_lib/type_investasi.ts +++ b/src/app_modules/investasi/_lib/type_investasi.ts @@ -23,4 +23,15 @@ export interface IDataInvestasiBursa { targetDana: string pencarianInvestor: string updatedAt: Date +} + +export interface IDataSahamSaya { + id: string + nominal: string + lembarTerbeli: string + statusInvoiceId: string + createdAt: Date + statusInvoice: string + title: string + progress: string } \ 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_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) => } +
+
+ ) + } + + ); +} From 2322ab8444ef8dd9b2c06a1e70cade691fb6bfc2 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 19 Dec 2024 06:34:12 +0800 Subject: [PATCH 081/595] Fix notifikasi server action to API --- src/app/api/collaboration/master/route.ts | 20 ++ src/app/api/notifikasi/count/route.ts | 28 +++ .../notifikasi/get-all-by-category/route.ts | 70 +++---- src/app/api/notifikasi/master/route.ts | 35 ++++ src/app/dev/(user)/home/page.tsx | 2 +- src/app/dev/colab/create/page.tsx | 5 +- src/app/dev/notifikasi/collaboration/page.tsx | 10 +- src/app/dev/notifikasi/donasi/page.tsx | 10 +- src/app/dev/notifikasi/event/page.tsx | 6 +- src/app/dev/notifikasi/forum/page.tsx | 10 +- src/app/dev/notifikasi/investasi/page.tsx | 10 +- src/app/dev/notifikasi/job/page.tsx | 10 +- src/app/dev/notifikasi/layout.tsx | 4 - src/app/dev/notifikasi/semua/page.tsx | 4 +- src/app/dev/notifikasi/voting/page.tsx | 10 +- .../api_user_router/route_api_notifikasi.ts | 4 + src/app/lib/global_state.ts | 2 +- src/app/lib/prisma.ts | 2 +- src/app_modules/colab/component/index.ts | 3 + .../colab/component/lib/api_collaboration.ts | 4 + .../colab/component/skeleton_view.tsx | 28 +++ src/app_modules/colab/create/index.tsx | 55 +++++- src/app_modules/home/component/body_home.tsx | 36 ++-- .../home/component/footer_home.tsx | 5 + src/app_modules/home/view_home_new.tsx | 134 +++++++++++++ src/app_modules/notifikasi/_ui/index.ts | 21 +-- .../notifikasi/_ui/ui_all_notifikasi.tsx | 92 --------- .../_ui/ui_collaboration_notifikasi.tsx | 61 ------ .../notifikasi/_ui/ui_donasi_notifikasi.tsx | 61 ------ .../notifikasi/_ui/ui_forum_notifikasi.tsx | 61 ------ .../_ui/ui_investasi_notifikasi.tsx | 61 ------ .../notifikasi/_ui/ui_job_notifikasi.tsx | 74 -------- .../_ui/ui_new_layout_notifikasi.tsx | 105 ++++++++--- .../notifikasi/_ui/ui_new_notifikasi.tsx | 80 -------- ...event_notifikasi.tsx => ui_notifikasi.tsx} | 43 +++-- .../notifikasi/_ui/ui_voting_notifikasi.tsx | 61 ------ .../notifikasi/lib/api_notifikasi.ts | 10 + src/app_modules/notifikasi/ui/index.ts | 5 - .../notifikasi/ui/ui_layout_notifikasi.tsx | 91 --------- .../notifikasi/ui/ui_notifiaksi.tsx | 176 ------------------ src/app_modules/notifikasi/view/index.ts | 3 - .../notifikasi/view/view_notifikasi.tsx | 25 --- src/middleware.ts | 1 + 43 files changed, 507 insertions(+), 1031 deletions(-) create mode 100644 src/app/api/collaboration/master/route.ts create mode 100644 src/app/api/notifikasi/count/route.ts create mode 100644 src/app/api/notifikasi/master/route.ts create mode 100644 src/app_modules/colab/component/index.ts create mode 100644 src/app_modules/colab/component/lib/api_collaboration.ts create mode 100644 src/app_modules/colab/component/skeleton_view.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_all_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_collaboration_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_donasi_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_forum_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_investasi_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_job_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/_ui/ui_new_notifikasi.tsx rename src/app_modules/notifikasi/_ui/{ui_event_notifikasi.tsx => ui_notifikasi.tsx} (69%) delete mode 100644 src/app_modules/notifikasi/_ui/ui_voting_notifikasi.tsx create mode 100644 src/app_modules/notifikasi/lib/api_notifikasi.ts delete mode 100644 src/app_modules/notifikasi/ui/index.ts delete mode 100644 src/app_modules/notifikasi/ui/ui_layout_notifikasi.tsx delete mode 100644 src/app_modules/notifikasi/ui/ui_notifiaksi.tsx delete mode 100644 src/app_modules/notifikasi/view/index.ts delete mode 100644 src/app_modules/notifikasi/view/view_notifikasi.tsx 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/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 index b13ce671..bfa9aefd 100644 --- a/src/app/api/notifikasi/get-all-by-category/route.ts +++ b/src/app/api/notifikasi/get-all-by-category/route.ts @@ -7,37 +7,34 @@ import { NextResponse } from "next/server"; export const dynamic = "force-dynamic"; export async function GET(request: Request) { - if (request.method === "GET") { - try { - const { searchParams } = new URL(request.url); - const category = searchParams.get("category") as ICategoryapp; - const page = searchParams.get("page"); + 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; + const userLoginId = await newFunGetUserId(); + const fixPage = _.toNumber(page); + const takeData = 10; + const skipData = fixPage * takeData - takeData; - if (category === "Semua") { - const data = await prisma.notifikasi.findMany({ - take: takeData, - skip: skipData, - orderBy: [ - { - isRead: "asc", - }, - { createdAt: "desc" }, - ], - where: { - userId: userLoginId, - userRoleId: "1", + if (category === "Semua") { + fixData = await prisma.notifikasi.findMany({ + take: takeData, + skip: skipData, + orderBy: [ + { + isRead: "asc", }, - }); - - return NextResponse.json({ success: true, data }); - } - - const allData = await prisma.notifikasi.findMany({ + { createdAt: "desc" }, + ], + where: { + userId: userLoginId, + userRoleId: "1", + }, + }); + } else { + fixData = await prisma.notifikasi.findMany({ take: takeData, skip: skipData, orderBy: [ @@ -52,12 +49,17 @@ export async function GET(request: Request) { kategoriApp: _.upperCase(category), }, }); - - return NextResponse.json({ success: true, data: allData }); - } catch (error) { - backendLogger.error("Error get data notifikasi: " + error); } - } else { - return NextResponse.json({ success: false, message: "Method not allowed" }); + + 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/dev/(user)/home/page.tsx b/src/app/dev/(user)/home/page.tsx index 4bd78e32..c0fdc4c2 100644 --- a/src/app/dev/(user)/home/page.tsx +++ b/src/app/dev/(user)/home/page.tsx @@ -15,7 +15,7 @@ export default async function PageHome() { dataJob={dataJob as any} countNotifikasi={countNotifikasi} /> */} - + ); } 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/notifikasi/collaboration/page.tsx b/src/app/dev/notifikasi/collaboration/page.tsx index edf580a3..ca9f64eb 100644 --- a/src/app/dev/notifikasi/collaboration/page.tsx +++ b/src/app/dev/notifikasi/collaboration/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiCollaboration } 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: "Collaboration", - }); - 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 3b7b8e34..d4f0d3a1 100644 --- a/src/app/dev/notifikasi/event/page.tsx +++ b/src/app/dev/notifikasi/event/page.tsx @@ -1,11 +1,9 @@ -import { Notifikasi_UiEvent } from "@/app_modules/notifikasi/_ui"; +import Notifikasi_UiMain from "@/app_modules/notifikasi/_ui/ui_notifikasi"; export default async function Page() { - // - 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 2f691b3c..ca9f64eb 100644 --- a/src/app/dev/notifikasi/semua/page.tsx +++ b/src/app/dev/notifikasi/semua/page.tsx @@ -1,9 +1,9 @@ -import { Notifikasi_UiAll } from "@/app_modules/notifikasi/_ui"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { 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 index 94027735..0fccc1b0 100644 --- a/src/app/lib/api_user_router/route_api_notifikasi.ts +++ b/src/app/lib/api_user_router/route_api_notifikasi.ts @@ -8,4 +8,8 @@ export const API_RouteNotifikasi = { 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 101fd93b..07ccb3e8 100644 --- a/src/app/lib/prisma.ts +++ b/src/app/lib/prisma.ts @@ -25,6 +25,6 @@ process.on("SIGINT", async () => { process.exit(0); }); -// console.log("==> Test prisma"); +console.log("==> Test prisma"); export default prisma; 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 ( <> - + - setFilter(e.target.value as any)} + className={styles.select} + > + + + + + + + +
+ {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/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/dev/(user)/home/page.tsx b/src/app/dev/(user)/home/page.tsx index c0fdc4c2..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/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 ( <> { 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_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/home/view_home_new.tsx b/src/app_modules/home/view_home_new.tsx index e383952e..b6f4dd51 100644 --- a/src/app_modules/home/view_home_new.tsx +++ b/src/app_modules/home/view_home_new.tsx @@ -1,5 +1,7 @@ "use client"; +import { API_RouteNotifikasi } from "@/app/lib/api_user_router/route_api_notifikasi"; import { gs_count_ntf, gs_user_ntf } from "@/app/lib/global_state"; +import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi"; import { RouterUserSearch } from "@/app/lib/router_hipmi/router_user_search"; import { ActionIcon, Indicator, Text } from "@mantine/core"; @@ -9,46 +11,35 @@ import { useAtom } from "jotai"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { MainColor } from "../_global/color"; -import { ComponentGlobal_NotifikasiPeringatan } from "../_global/notif_global"; import UIGlobal_LayoutHeaderTamplate from "../_global/ui/ui_header_tamplate"; import UIGlobal_LayoutTamplate from "../_global/ui/ui_layout_tamplate"; -import notifikasi_countUserNotifikasi from "../notifikasi/fun/count/fun_count_by_id"; +import { gs_notifikasi_kategori_app } from "../notifikasi/lib"; import BodyHome from "./component/body_home"; import FooterHome from "./component/footer_home"; import { apiGetDataHome } from "./fun/get/api_home"; -import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; -import { gs_notifikasi_kategori_app } from "../notifikasi/lib"; -export default function HomeViewNew({ - countNotifikasi, -}: { - countNotifikasi: number; -}) { - const [countNtf, setCountNtf] = useState(countNotifikasi); +export default function HomeViewNew() { + const [countNtf, setCountNtf] = useAtom(gs_count_ntf); const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); - const [countLoadNtf, setCountLoadNtf] = useAtom(gs_count_ntf); const [dataUser, setDataUser] = useState({}); const [categoryPage, setCategoryPage] = useAtom(gs_notifikasi_kategori_app); const router = useRouter(); useShallowEffect(() => { - onLoadNotifikasi({ - onLoad(val) { - setCountNtf(val); - }, - }); - - setCountNtf(countLoadNtf as any); - }, [countLoadNtf, setCountNtf]); + onLoadNotifikasi(); + }, []); useShallowEffect(() => { - setCountNtf(countNtf + newUserNtf); - setNewUserNtf(0); - }, [newUserNtf, setCountNtf]); + if (countNtf != null) { + setCountNtf(countNtf + newUserNtf); + setNewUserNtf(0); + } + }, [newUserNtf, countNtf]); - async function onLoadNotifikasi({ onLoad }: { onLoad: (val: any) => void }) { - const loadNotif = await notifikasi_countUserNotifikasi(); - onLoad(loadNotif); + async function onLoadNotifikasi() { + const loadNotif = await fetch(API_RouteNotifikasi.get_count_by_id()); + const data = await loadNotif.json().then((res) => res.data); + setCountNtf(data); } useShallowEffect(() => { @@ -75,6 +66,7 @@ export default function HomeViewNew({ customButtonLeft={ { if ( @@ -93,6 +85,7 @@ export default function HomeViewNew({ customButtonRight={ { if ( dataUser.profile === undefined || @@ -100,24 +93,23 @@ export default function HomeViewNew({ ) { router.push(RouterProfile.create, { scroll: false }); } else { - setCategoryPage("Semua") + setCategoryPage("Semua"); router.push( RouterNotifikasi.categoryApp({ name: "semua" }), { scroll: false, } ); - } }} > - {countNotifikasi > 0 ? ( + {countNtf != null && countNtf > 0 ? ( - {countNotifikasi > 99 ? "99+" : countNotifikasi} + {countNtf > 99 ? "99+" : countNtf} } > @@ -137,137 +129,3 @@ export default function HomeViewNew({ ); } - -// "use client"; -// import { API_RouteNotifikasi } from "@/app/lib/api_user_router/route_api_notifikasi"; -// import { gs_count_ntf, gs_user_ntf } from "@/app/lib/global_state"; -// import { RouterProfile } from "@/app/lib/router_hipmi/router_katalog"; -// import { RouterNotifikasi } from "@/app/lib/router_hipmi/router_notifikasi"; -// import { RouterUserSearch } from "@/app/lib/router_hipmi/router_user_search"; -// import { ActionIcon, Indicator, Text } from "@mantine/core"; -// import { useShallowEffect } from "@mantine/hooks"; -// import { IconBell, IconUserSearch } from "@tabler/icons-react"; -// import { useAtom } from "jotai"; -// import { useRouter } from "next/navigation"; -// import { useState } from "react"; -// import { MainColor } from "../_global/color"; -// import UIGlobal_LayoutHeaderTamplate from "../_global/ui/ui_header_tamplate"; -// import UIGlobal_LayoutTamplate from "../_global/ui/ui_layout_tamplate"; -// import { gs_notifikasi_kategori_app } from "../notifikasi/lib"; -// import BodyHome from "./component/body_home"; -// import FooterHome from "./component/footer_home"; -// import { apiGetDataHome } from "./fun/get/api_home"; - -// export default function HomeViewNew() { -// const [countNtf, setCountNtf] = useAtom(gs_count_ntf); -// const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); -// const [dataUser, setDataUser] = useState({}); -// const [categoryPage, setCategoryPage] = useAtom(gs_notifikasi_kategori_app); -// const router = useRouter(); - -// useShallowEffect(() => { -// onLoadNotifikasi(); -// }, []); - -// useShallowEffect(() => { -// if (countNtf != null) { -// setCountNtf(countNtf + newUserNtf); -// setNewUserNtf(0); -// } -// console.log("notif baru", newUserNtf); -// console.log("notif baru", countNtf); -// }, [newUserNtf, countNtf]); - -// async function onLoadNotifikasi() { -// const loadNotif = await fetch(API_RouteNotifikasi.get_count_by_id()); -// const data = await loadNotif.json().then((res) => res.data); -// setCountNtf(data); -// } - -// useShallowEffect(() => { -// cekUserLogin(); -// }, []); - -// async function cekUserLogin() { -// try { -// const response = await apiGetDataHome("?cat=cek_profile"); -// if (response.success) { -// setDataUser(response.data); -// } -// } catch (error) { -// console.error(error); -// } -// } - -// return ( -// <> -// { -// if ( -// dataUser.profile === undefined || -// dataUser?.profile === null -// ) { -// router.push(RouterProfile.create, { scroll: false }); -// } else { -// router.push(RouterUserSearch.main, { scroll: false }); -// } -// }} -// > -// -// -// } -// customButtonRight={ -// { -// if ( -// dataUser.profile === undefined || -// dataUser?.profile === null -// ) { -// router.push(RouterProfile.create, { scroll: false }); -// } else { -// setCategoryPage("Semua"); -// router.push( -// RouterNotifikasi.categoryApp({ name: "semua" }), -// { -// scroll: false, -// } -// ); -// } -// }} -// > -// {countNtf != null && countNtf > 0 ? ( -// -// {countNtf > 99 ? "99+" : countNtf} -// -// } -// > -// -// -// ) : ( -// -// )} -// -// } -// /> -// } -// footer={} -// > -// -// -// -// ); -// } From 32b44fad794640bbd6352b05165da3904781d677 Mon Sep 17 00:00:00 2001 From: amel Date: Thu, 19 Dec 2024 15:40:16 +0800 Subject: [PATCH 084/595] upd : edit donasi Deskripsi: - ubah api master pada edit donasi - get one edit donasi No Issues --- src/app/api/new/donasi/[id]/route.ts | 34 ++ .../dev/donasi/edit/edit_donasi/[id]/page.tsx | 13 +- .../edit/edit_donasi/edit_donasi_new.tsx | 336 ++++++++++++++++++ .../edit/edit_donasi/skeleton_edit_donasi.tsx | 23 ++ src/app_modules/donasi/index.ts | 4 +- src/app_modules/donasi/lib/api_donasi.ts | 5 + 6 files changed, 408 insertions(+), 7 deletions(-) create mode 100644 src/app/api/new/donasi/[id]/route.ts create mode 100644 src/app_modules/donasi/edit/edit_donasi/edit_donasi_new.tsx create mode 100644 src/app_modules/donasi/edit/edit_donasi/skeleton_edit_donasi.tsx diff --git a/src/app/api/new/donasi/[id]/route.ts b/src/app/api/new/donasi/[id]/route.ts new file mode 100644 index 00000000..64c367c0 --- /dev/null +++ b/src/app/api/new/donasi/[id]/route.ts @@ -0,0 +1,34 @@ +import { prisma } from "@/app/lib"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + +// GET ONE DATA DONASI +export async function GET(request: Request, context: { params: { id: string } }) { + try { + const { id } = context.params + const data = await prisma.donasi.findFirst({ + where: { + id: id + }, + include: { + Author: true, + imageDonasi: true, + CeritaDonasi: true, + DonasiMaster_Ketegori: true, + DonasiMaster_Durasi: true, + DonasiMaster_Status: true, + Donasi_Invoice: true, + Donasi_Kabar: true, + Donasi_PencairanDana: 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/dev/donasi/edit/edit_donasi/[id]/page.tsx b/src/app/dev/donasi/edit/edit_donasi/[id]/page.tsx index cb62d88a..923fdcf9 100644 --- a/src/app/dev/donasi/edit/edit_donasi/[id]/page.tsx +++ b/src/app/dev/donasi/edit/edit_donasi/[id]/page.tsx @@ -1,15 +1,16 @@ -import { EditDonasi } from "@/app_modules/donasi"; +import { EditDonasi, EditDonasiNew } from "@/app_modules/donasi"; import { Donasi_getMasterDurasi, Donasi_getMasterKategori } from "@/app_modules/donasi/fun"; import { Donasi_getOneById } from "@/app_modules/donasi/fun/get/get_one_donasi_by_id"; -export default async function Page({params}: {params: {id: string}}) { - const dataDonasi = await Donasi_getOneById(params.id) - const masterKategori = await Donasi_getMasterKategori() - const masterDurasi = await Donasi_getMasterDurasi() +export default async function Page({ params }: { params: { id: string } }) { + // const dataDonasi = await Donasi_getOneById(params.id) + // const masterKategori = await Donasi_getMasterKategori() + // const masterDurasi = await Donasi_getMasterDurasi() return ( <> - + {/* */} + ); } diff --git a/src/app_modules/donasi/edit/edit_donasi/edit_donasi_new.tsx b/src/app_modules/donasi/edit/edit_donasi/edit_donasi_new.tsx new file mode 100644 index 00000000..40d136b4 --- /dev/null +++ b/src/app_modules/donasi/edit/edit_donasi/edit_donasi_new.tsx @@ -0,0 +1,336 @@ +"use client"; +import { DIRECTORY_ID } from "@/app/lib"; +import { MainColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_BoxUploadImage, ComponentGlobal_LoadImageCustom, } from "@/app_modules/_global/component"; +import ComponentGlobal_ErrorInput from "@/app_modules/_global/component/error_input"; +import { funGlobal_DeleteFileById, funGlobal_UploadToStorage, } from "@/app_modules/_global/fun"; +import { ComponentGlobal_NotifikasiBerhasil, ComponentGlobal_NotifikasiPeringatan, } from "@/app_modules/_global/notif_global"; +import { AspectRatio, Button, Center, FileButton, Image, Select, Stack, Text, TextInput, } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconCamera } from "@tabler/icons-react"; +import _ from "lodash"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; +import { Donasi_funUpdateDonasi } from "../../fun/update/fun_update_donasi"; +import { apiGetMasterDonasi, apiGetOneDonasiById } from "../../lib/api_donasi"; +import { MODEL_DONASI } from "../../model/interface"; +import SkeletonEditDonasi from "./skeleton_edit_donasi"; + +export default function EditDonasiNew() { + const router = useRouter(); + const [isLoading, setLoading] = useState(false); + const [data, setData] = useState(); + const [kategori, setKategori] = useState([]); + const [durasi, setDurasi] = useState([]); + const [file, setFile] = useState(null); + const [updateImage, setUpdateImage] = useState(); + const [newTarget, setNewTarget] = useState(""); + const [loadingMaster, setLoadingMaster] = useState(true) + const param = useParams<{ id: string }>(); + const [loadingData, setLoadingData] = useState(true) + + + async function onGetMaster() { + try { + setLoadingMaster(true) + const responseKategori = await apiGetMasterDonasi("?cat=kategori") + const responseDurasi = await apiGetMasterDonasi("?cat=durasi") + if (responseKategori.success) { + setKategori(responseKategori.data) + } + if (responseDurasi.success) { + setDurasi(responseDurasi.data) + } + } catch (error) { + console.log(error); + } finally { + setLoadingMaster(false) + } + } + + async function onGetData() { + try { + setLoadingData(true) + const response = await apiGetOneDonasiById(param.id) + if (response.success) { + setData(response.data) + } + + } catch (error) { + console.error(error); + } finally { + setLoadingData(false) + } + } + + useShallowEffect(() => { + onGetMaster() + onGetData() + }, []) + + async function onUpdate() { + setLoading(true); + const body = { + id: data?.id, + donasiMaster_KategoriId: data?.DonasiMaster_Ketegori.id, + donasiMaster_DurasiId: data?.DonasiMaster_Durasi.id, + title: data?.title, + target: data?.target, + }; + + if (_.values(body).includes("")) + return ComponentGlobal_NotifikasiPeringatan("Lengkapin Data"); + + try { + if (file !== null) { + const uploadImage = await funGlobal_UploadToStorage({ + file: file as File, + dirId: DIRECTORY_ID.donasi_image, + }); + if (!uploadImage.success) { + setLoading(false); + ComponentGlobal_NotifikasiPeringatan("Gagal upload file gambar"); + } + + const deleteImage = await funGlobal_DeleteFileById({ + fileId: String(data?.imageId), + }); + if (!deleteImage.success) { + setLoading(false); + ComponentGlobal_NotifikasiPeringatan("Gagal hapus gambar lama"); + } + + const res = await Donasi_funUpdateDonasi({ + data: body as any, + fileId: uploadImage.data.id, + }); + if (res.status === 200) { + ComponentGlobal_NotifikasiBerhasil(res.message); + router.back(); + setLoading(false); + } else { + ComponentGlobal_NotifikasiPeringatan(res.message); + setLoading(false); + } + } else { + const res = await Donasi_funUpdateDonasi({ + data: body as any, + }); + if (res.status === 200) { + ComponentGlobal_NotifikasiBerhasil(res.message); + router.back(); + setLoading(false); + } else { + ComponentGlobal_NotifikasiPeringatan(res.message); + setLoading(false); + } + } + } catch (error) { + console.log(error); + } + } + + return ( + <> + { + loadingData ? + : + + ({ + value: e.id, + label: e.name + " " + `hari`, + }))} + onChange={(val) => + setData({ + ...(data as any), + DonasiMaster_Durasi: { + id: val, + }, + }) + } + /> + + +
+ + } + + + ); +} diff --git a/src/app_modules/donasi/edit/edit_donasi/skeleton_edit_donasi.tsx b/src/app_modules/donasi/edit/edit_donasi/skeleton_edit_donasi.tsx new file mode 100644 index 00000000..922e275e --- /dev/null +++ b/src/app_modules/donasi/edit/edit_donasi/skeleton_edit_donasi.tsx @@ -0,0 +1,23 @@ +import { Box, Skeleton, Stack } from "@mantine/core"; + +export default function SkeletonEditDonasi() { + return ( + <> + + + + + + + + + {[...Array(5)].map((_, index) => ( + + ))} + + + + + + ); +} \ No newline at end of file diff --git a/src/app_modules/donasi/index.ts b/src/app_modules/donasi/index.ts index f5d776c2..65fde92b 100644 --- a/src/app_modules/donasi/index.ts +++ b/src/app_modules/donasi/index.ts @@ -50,6 +50,7 @@ import Donasi_CreateKabar from "./create/create_kabar"; import LayoutDonasi_CreateKabar from "./create/create_kabar/layout"; import Donasi_EditRekening from "./edit/edit_rekening"; import LayoutDonasi_EditRekening from "./edit/edit_rekening/layout"; +import EditDonasiNew from "./edit/edit_donasi/edit_donasi_new"; export { LayoutDonasi_BuktiTransfer } from "./detail/detail_main/bukti_transfer"; export { @@ -104,5 +105,6 @@ export { LayoutDonasi_EditRekening, MainDonasiNew, GalangDanaDonasiNew, - CreateDonasiNew + CreateDonasiNew, + EditDonasiNew }; diff --git a/src/app_modules/donasi/lib/api_donasi.ts b/src/app_modules/donasi/lib/api_donasi.ts index 2f7e719e..b42ce605 100644 --- a/src/app_modules/donasi/lib/api_donasi.ts +++ b/src/app_modules/donasi/lib/api_donasi.ts @@ -11,4 +11,9 @@ export const apiGetMasterDonasi = async (path?: string) => { export const apiGetAllDonasiSaya = async (path?: string) => { const response = await fetch(`/api/new/donasi/invoice${(path) ? path : ''}`) return await response.json().catch(() => null) +} + +export const apiGetOneDonasiById = async (path: string) => { + const response = await fetch(`/api/new/donasi/${path}`) + return await response.json().catch(() => null) } \ No newline at end of file From 692dd2560934782b551c611b25951cbc5a1194b2 Mon Sep 17 00:00:00 2001 From: amel Date: Fri, 20 Dec 2024 17:30:46 +0800 Subject: [PATCH 085/595] upd: donasi Deskripsi: - api detail donasi No Issues --- src/app/api/new/donasi/[id]/route.ts | 81 ++++++-- .../dev/donasi/detail/main/[id]/layout.tsx | 28 ++- src/app/dev/donasi/detail/main/[id]/page.tsx | 18 +- .../detail_main/cerita_penggalang_new.tsx | 89 +++++++++ .../detail_main/detail_dana_donasi_new.tsx | 175 ++++++++++++++++++ .../detail_main/informasi_penggalang_new.tsx | 97 ++++++++++ .../skeleton_cerita_penggalang.tsx | 14 ++ .../skeleton_detail_dana_donasi.tsx | 21 +++ .../donasi/detail/detail_main/index_new.tsx | 17 ++ .../donasi/detail/detail_main/layout_new.tsx | 85 +++++++++ .../edit/edit_donasi/edit_donasi_new.tsx | 2 +- src/app_modules/donasi/index.ts | 4 +- src/app_modules/donasi/lib/api_donasi.ts | 4 +- 13 files changed, 588 insertions(+), 47 deletions(-) create mode 100644 src/app_modules/donasi/component/detail_main/cerita_penggalang_new.tsx create mode 100644 src/app_modules/donasi/component/detail_main/detail_dana_donasi_new.tsx create mode 100644 src/app_modules/donasi/component/detail_main/informasi_penggalang_new.tsx create mode 100644 src/app_modules/donasi/component/detail_main/skeleton_cerita_penggalang.tsx create mode 100644 src/app_modules/donasi/component/detail_main/skeleton_detail_dana_donasi.tsx create mode 100644 src/app_modules/donasi/detail/detail_main/index_new.tsx create mode 100644 src/app_modules/donasi/detail/detail_main/layout_new.tsx diff --git a/src/app/api/new/donasi/[id]/route.ts b/src/app/api/new/donasi/[id]/route.ts index 64c367c0..96da4a1e 100644 --- a/src/app/api/new/donasi/[id]/route.ts +++ b/src/app/api/new/donasi/[id]/route.ts @@ -6,25 +6,72 @@ export const dynamic = "force-dynamic"; // GET ONE DATA DONASI export async function GET(request: Request, context: { params: { id: string } }) { try { + let dataFix const { id } = context.params - const data = await prisma.donasi.findFirst({ - where: { - id: id - }, - include: { - Author: true, - imageDonasi: true, - CeritaDonasi: true, - DonasiMaster_Ketegori: true, - DonasiMaster_Durasi: true, - DonasiMaster_Status: true, - Donasi_Invoice: true, - Donasi_Kabar: true, - Donasi_PencairanDana: true, - }, - }); + const { searchParams } = new URL(request.url) + const kategori = searchParams.get("cat") - return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data }, { status: 200 }); + if (kategori == "semua") { + dataFix = await prisma.donasi.findFirst({ + where: { + id: id + }, + include: { + Author: true, + imageDonasi: true, + CeritaDonasi: true, + DonasiMaster_Ketegori: true, + DonasiMaster_Durasi: true, + DonasiMaster_Status: true, + Donasi_Invoice: true, + Donasi_Kabar: true, + Donasi_PencairanDana: true, + }, + }); + + } else if (kategori == "count") { + dataFix = await prisma.donasi_Invoice.count({ + where: { + donasiId: id, + donasiMaster_StatusInvoiceId: { + equals: "1" + } + } + }); + + } else { + let tampil + if (kategori == "author") { + tampil = { + authorId: true, + Author: { + select: { + username: true + } + } + } + } else if (kategori == "cerita") { + tampil = { + id: true, + createdAt: true, + CeritaDonasi: { + select: { + cerita: true + } + } + } + } + + dataFix = await prisma.donasi.findFirst({ + where: { + id: id + }, + select: tampil + }) + } + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); } catch (error) { diff --git a/src/app/dev/donasi/detail/main/[id]/layout.tsx b/src/app/dev/donasi/detail/main/[id]/layout.tsx index ad394a61..77eff8c2 100644 --- a/src/app/dev/donasi/detail/main/[id]/layout.tsx +++ b/src/app/dev/donasi/detail/main/[id]/layout.tsx @@ -1,29 +1,25 @@ -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import { LayoutDetailMainDonasi } from "@/app_modules/donasi"; -import { Donasi_getOneById } from "@/app_modules/donasi/fun/get/get_one_donasi_by_id"; +import { LayoutDetailMainDonasiNew } from "@/app_modules/donasi"; import React from "react"; -export default async function Layout({ - children, - params, -}: { - children: React.ReactNode; - params: { id: string }; -}) { - const donasiId = params.id; - const getData = await Donasi_getOneById(donasiId); - const authorId = getData?.authorId; - const userLoginId = await funGetUserIdByToken(); +export default async function Layout({ children, params, }: { children: React.ReactNode; params: { id: string }; }) { + // const donasiId = params.id; + // const getData = await Donasi_getOneById(donasiId); + // const authorId = getData?.authorId; + // const userLoginId = await funGetUserIdByToken(); return ( <> - {children} - + */} + + + {children} + ); } diff --git a/src/app/dev/donasi/detail/main/[id]/page.tsx b/src/app/dev/donasi/detail/main/[id]/page.tsx index 6ea7e94c..72a2bbd2 100644 --- a/src/app/dev/donasi/detail/main/[id]/page.tsx +++ b/src/app/dev/donasi/detail/main/[id]/page.tsx @@ -1,22 +1,20 @@ -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import { DetailMainDonasi } from "@/app_modules/donasi"; -import { Donasi_getCountDonatur } from "@/app_modules/donasi/fun/count/get_count_donatur"; -import { Donasi_getOneById } from "@/app_modules/donasi/fun/get/get_one_donasi_by_id"; +import DetailMainDonasiNew from "@/app_modules/donasi/detail/detail_main/index_new"; export default async function Page({ params }: { params: { id: string } }) { - let donasiId = params.id; - const userLoginId = await funGetUserIdByToken(); + // let donasiId = params.id; + // const userLoginId = await funGetUserIdByToken(); - const dataDonasi = await Donasi_getOneById(donasiId); - const countDonatur = await Donasi_getCountDonatur(donasiId); + // const dataDonasi = await Donasi_getOneById(donasiId); + // const countDonatur = await Donasi_getCountDonatur(donasiId); return ( <> - + /> */} + ); } diff --git a/src/app_modules/donasi/component/detail_main/cerita_penggalang_new.tsx b/src/app_modules/donasi/component/detail_main/cerita_penggalang_new.tsx new file mode 100644 index 00000000..d0b52bd4 --- /dev/null +++ b/src/app_modules/donasi/component/detail_main/cerita_penggalang_new.tsx @@ -0,0 +1,89 @@ +"use client"; +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { AccentColor, MainColor, } from "@/app_modules/_global/color/color_pallet"; +import { ActionIcon, Group, Paper, Stack, Text, Title } from "@mantine/core"; +import { IconCircleChevronRight } from "@tabler/icons-react"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; +import { apiGetOneDonasiById } from "../../lib/api_donasi"; +import { useShallowEffect } from "@mantine/hooks"; +import SkeletonCeritaPenggalangDonasi from "./skeleton_cerita_penggalang"; + +export default function ComponentDonasi_CeritaPenggalangMainNew() { + const router = useRouter(); + const param = useParams<{ id: string }>(); + const [loading, setLoading] = useState(true) + const [donasi, setDonasi] = useState({}) + + + async function getCeritaDonasi() { + try { + setLoading(true) + const response = await apiGetOneDonasiById(param.id, "cerita") + if (response.success) { + setDonasi(response.data) + }else{ + + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getCeritaDonasi() + }, []) + + + return ( + <> + + Cerita Penggalang Dana + + { + loading ? + : + + + + {new Intl.DateTimeFormat("id-ID", { dateStyle: "full" }).format( + new Date(donasi?.createdAt) + )} + + { + router.push(RouterDonasi.cerita_penggalang + `${donasi?.id}`); + }} + > + + + + {donasi?.CeritaDonasi.cerita} + + } + + + + ); +} diff --git a/src/app_modules/donasi/component/detail_main/detail_dana_donasi_new.tsx b/src/app_modules/donasi/component/detail_main/detail_dana_donasi_new.tsx new file mode 100644 index 00000000..dab456bf --- /dev/null +++ b/src/app_modules/donasi/component/detail_main/detail_dana_donasi_new.tsx @@ -0,0 +1,175 @@ +"use client"; +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { MainColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_CardStyles, ComponentGlobal_LoadImageLandscape, } from "@/app_modules/_global/component"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { Divider, Grid, Group, Progress, Stack, Text, Title, } from "@mantine/core"; +import { IconClover, IconMessageChatbot, IconMoneybag, } from "@tabler/icons-react"; +import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; +import { useParams, useRouter } from "next/navigation"; +import { Donasi_findDonaturByTokenId } from "../../fun/get/get_donatur_by_token_id"; +import { MODEL_DONASI } from "../../model/interface"; +import ComponentDonasi_TampilanHitungMundur from "../tampilan_hitung_mundur"; +import TampilanRupiahDonasi from "../tampilan_rupiah"; +import { useState } from "react"; +import { apiGetOneDonasiById } from "../../lib/api_donasi"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import { useShallowEffect } from "@mantine/hooks"; +import SkeletonDetailDanaDonasi from "./skeleton_detail_dana_donasi"; + +export function ComponentDonasi_DetailDataMainNew() { + const router = useRouter(); + const param = useParams<{ id: string }>(); + const [loading, setLoading] = useState(true); + const [donasi, setDonasi] = useState(); + const [countDonatur, setcountDonatur] = useState(0); + const [userLoginId, setuserLoginId] = useState(""); + + async function getData() { + try { + setLoading(true) + const response = await apiGetOneDonasiById(param.id, "semua") + const response1 = await apiGetOneDonasiById(param.id, "count") + const response2 = await funGetUserIdByToken() + if (response.success) { + setDonasi(response.data) + setcountDonatur(response1.data) + setuserLoginId(response2) + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + useShallowEffect(() => { + getData() + }, []) + + return ( + <> + + { + loading ? + : + + + + {donasi?.title} + + + + + + Dana terkumpul + + <TampilanRupiahDonasi nominal={+donasi?.terkumpul} /> + + + Dari target{" "} + + + + + Kategori + + {donasi?.DonasiMaster_Ketegori.name} + + + + + + + + { + router.push(RouterDonasi.donatur + `${donasi.id}`); + }} + > + + + + {countDonatur} + + + Donatur + + + + + { + router.push(RouterDonasi.daftar_kabar({ id: donasi.id }), { + scroll: false, + }); + }} + > + + + Kabar Terbaru + + + + { + onPencairanDana( + router, + donasi, + userLoginId, + ); + }} + > + + + Pencairan Dana + + + + + } + + + ); +} + +async function onPencairanDana(router: AppRouterInstance, donasi: MODEL_DONASI, userLoginId: string,) { + const cek = await Donasi_findDonaturByTokenId(donasi.id, userLoginId); + if (userLoginId == donasi.authorId) { + return router.push(RouterDonasi.pencairan_dana + `${donasi.id}`); + } + + if (!cek) { + return ComponentGlobal_NotifikasiPeringatan("Halaman khusus donatur"); + } else { + router.push(RouterDonasi.pencairan_dana + `${donasi.id}`); + } +} diff --git a/src/app_modules/donasi/component/detail_main/informasi_penggalang_new.tsx b/src/app_modules/donasi/component/detail_main/informasi_penggalang_new.tsx new file mode 100644 index 00000000..433fff92 --- /dev/null +++ b/src/app_modules/donasi/component/detail_main/informasi_penggalang_new.tsx @@ -0,0 +1,97 @@ +"use client"; +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { AccentColor, MainColor, } from "@/app_modules/_global/color/color_pallet"; +import ComponentGlobal_BoxInformation from "@/app_modules/_global/component/box_information"; +import { ActionIcon, Avatar, Group, Paper, Skeleton, Stack, Text, Title, } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconCircleChevronRight } from "@tabler/icons-react"; +import _ from "lodash"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; +import { apiGetOneDonasiById } from "../../lib/api_donasi"; + +export default function ComponentDonasi_InformasiPenggalangMainNew() { + const router = useRouter(); + const param = useParams<{ id: string }>(); + const [loading, setLoading] = useState(true); + const [data, setData] = useState({}) + + + async function getDataAuthor() { + try { + setLoading(true) + const response = await apiGetOneDonasiById(param.id, "author") + if (response.success) { + setData(response.data) + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + useShallowEffect(() => { + getDataAuthor() + }, []) + + return ( + <> + + Informasi Penggalang Dana + + { + loading ? + : + + + Penggalang Dana + { + router.push(RouterDonasi.penggalang_dana + `${data.authorId}`); + }} + > + + + + + + {(() => { + const usr = data?.Author?.username; + const splt = usr?.split(""); + const Up = _.upperCase(splt[0]) + + return Up; + })()} + + {data?.Author?.username} + + + + } + + + + ); +} diff --git a/src/app_modules/donasi/component/detail_main/skeleton_cerita_penggalang.tsx b/src/app_modules/donasi/component/detail_main/skeleton_cerita_penggalang.tsx new file mode 100644 index 00000000..3c3eb2d7 --- /dev/null +++ b/src/app_modules/donasi/component/detail_main/skeleton_cerita_penggalang.tsx @@ -0,0 +1,14 @@ +import { Box, Skeleton } from "@mantine/core"; + +export default function SkeletonCeritaPenggalangDonasi() { + return ( + <> + + + + + + + + ); +} \ No newline at end of file diff --git a/src/app_modules/donasi/component/detail_main/skeleton_detail_dana_donasi.tsx b/src/app_modules/donasi/component/detail_main/skeleton_detail_dana_donasi.tsx new file mode 100644 index 00000000..a8f287a6 --- /dev/null +++ b/src/app_modules/donasi/component/detail_main/skeleton_detail_dana_donasi.tsx @@ -0,0 +1,21 @@ +import { Box, Skeleton } from "@mantine/core"; + +export default function SkeletonDetailDanaDonasi() { + return ( + <> + + + + + + + + + + + + + + + ); +} \ No newline at end of file diff --git a/src/app_modules/donasi/detail/detail_main/index_new.tsx b/src/app_modules/donasi/detail/detail_main/index_new.tsx new file mode 100644 index 00000000..e2583f4e --- /dev/null +++ b/src/app_modules/donasi/detail/detail_main/index_new.tsx @@ -0,0 +1,17 @@ +"use client"; +import { Stack } from "@mantine/core"; +import ComponentDonasi_CeritaPenggalangMainNew from "../../component/detail_main/cerita_penggalang_new"; +import { ComponentDonasi_DetailDataMainNew } from "../../component/detail_main/detail_dana_donasi_new"; +import ComponentDonasi_InformasiPenggalangMainNew from "../../component/detail_main/informasi_penggalang_new"; + +export default function DetailMainDonasiNew() { + return ( + <> + + + + + + + ); +} diff --git a/src/app_modules/donasi/detail/detail_main/layout_new.tsx b/src/app_modules/donasi/detail/detail_main/layout_new.tsx new file mode 100644 index 00000000..2d85b04f --- /dev/null +++ b/src/app_modules/donasi/detail/detail_main/layout_new.tsx @@ -0,0 +1,85 @@ +"use client"; +import UIGlobal_LayoutHeaderTamplate from "@/app_modules/_global/ui/ui_header_tamplate"; +import UIGlobal_LayoutTamplate from "@/app_modules/_global/ui/ui_layout_tamplate"; +import React, { useState } from "react"; +import ButtonDonasi from "../../component/footer_button_donasi"; +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { IconDotsVertical, IconMessageShare } from "@tabler/icons-react"; +import { UIGlobal_Drawer } from "@/app_modules/_global/ui"; +import { ActionIcon } from "@mantine/core"; +import { useParams } from "next/navigation"; +import { apiGetOneDonasiById } from "../../lib/api_donasi"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import { useShallowEffect } from "@mantine/hooks"; + +export default function LayoutDetailMainDonasiNew({ children, }: { children: React.ReactNode; }) { + const [openDrawer, setOpenDrawer] = useState(false); + const param = useParams<{ id: string }>(); + const [loading, setLoading] = useState(true); + const [authorId, setAuthorId] = useState(""); + const [userLogin, setUserLogin] = useState(""); + + const listPage = [ + { + id: "1", + name: "Rekap Kabar", + icon: , + path: RouterDonasi.rekap_kabar({ id: param.id }), + }, + ]; + + async function getDataDonasi() { + try { + setLoading(true) + const response = await apiGetOneDonasiById(param.id, "author") + const response2 = await funGetUserIdByToken() + if (response.success) { + setAuthorId(response.data.authorId) + setUserLogin(response2) + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + useShallowEffect(() => { + getDataDonasi() + }, []) + + return ( + <> + setOpenDrawer(true)} + > + + + ) + } + /> + } + footer={} + > + {children} + + + setOpenDrawer(false)} + component={listPage} + /> + + ); +} diff --git a/src/app_modules/donasi/edit/edit_donasi/edit_donasi_new.tsx b/src/app_modules/donasi/edit/edit_donasi/edit_donasi_new.tsx index 40d136b4..28041831 100644 --- a/src/app_modules/donasi/edit/edit_donasi/edit_donasi_new.tsx +++ b/src/app_modules/donasi/edit/edit_donasi/edit_donasi_new.tsx @@ -51,7 +51,7 @@ export default function EditDonasiNew() { async function onGetData() { try { setLoadingData(true) - const response = await apiGetOneDonasiById(param.id) + const response = await apiGetOneDonasiById(param.id, "semua") if (response.success) { setData(response.data) } diff --git a/src/app_modules/donasi/index.ts b/src/app_modules/donasi/index.ts index 65fde92b..5751445d 100644 --- a/src/app_modules/donasi/index.ts +++ b/src/app_modules/donasi/index.ts @@ -1,3 +1,4 @@ +import LayoutDetailMainDonasiNew from '@/app_modules/donasi/detail/detail_main/layout_new'; import MainDonasi from "./main/beranda"; import MainDonasiNew from "./main/beranda_new"; import LayoutDonasi from "./main/layout"; @@ -106,5 +107,6 @@ export { MainDonasiNew, GalangDanaDonasiNew, CreateDonasiNew, - EditDonasiNew + EditDonasiNew, + LayoutDetailMainDonasiNew }; diff --git a/src/app_modules/donasi/lib/api_donasi.ts b/src/app_modules/donasi/lib/api_donasi.ts index b42ce605..a04b7563 100644 --- a/src/app_modules/donasi/lib/api_donasi.ts +++ b/src/app_modules/donasi/lib/api_donasi.ts @@ -13,7 +13,7 @@ export const apiGetAllDonasiSaya = async (path?: string) => { return await response.json().catch(() => null) } -export const apiGetOneDonasiById = async (path: string) => { - const response = await fetch(`/api/new/donasi/${path}`) +export const apiGetOneDonasiById = async (path: string, kategori: string) => { + const response = await fetch(`/api/new/donasi/${path}?cat=${kategori}`) return await response.json().catch(() => null) } \ No newline at end of file From 1fac36336c40103d972dd2526e5e63ae60933051 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 23 Dec 2024 07:51:35 +0800 Subject: [PATCH 086/595] Fix investasi Deksripsi: - Fix notifikasi --- .../dev/investasi/create/investasi/page.tsx | 11 - src/app/lib/global_state.ts | 4 + src/app/lib/realtime_provider.tsx | 34 +- src/app_modules/_global/fun/index.ts | 2 + .../fun_validasi_upload_created_file.ts | 106 +++ .../admin/investasi/detail/detail_review.tsx | 57 +- .../admin/investasi/main/table_review.tsx | 81 +- .../fun/get/fun_investasi_check_status.ts | 23 + .../notifikasi/route_setting/investasi.ts | 117 ++- .../admin/notifikasi/view_card_drawer.tsx | 67 +- src/app_modules/home/view_home_new.tsx | 16 +- .../comp_button_create_new_investasi.tsx | 140 +-- .../_component/main/comp_update_beranda.tsx | 20 +- .../investasi/_view/main/view_beranda_new.tsx | 145 +-- src/app_modules/investasi/create/view_new.tsx | 846 ++++++++++-------- src/app_modules/job/create/view.tsx | 13 + .../notifikasi/component/card_view.tsx | 79 +- .../notifikasi/component/path/investasi.ts | 72 +- .../fun/check/fun_check_investasi_status.ts | 31 + 19 files changed, 1145 insertions(+), 719 deletions(-) create mode 100644 src/app_modules/_global/fun/upload/fun_validasi_upload_created_file.ts create mode 100644 src/app_modules/admin/notifikasi/fun/get/fun_investasi_check_status.ts create mode 100644 src/app_modules/notifikasi/fun/check/fun_check_investasi_status.ts diff --git a/src/app/dev/investasi/create/investasi/page.tsx b/src/app/dev/investasi/create/investasi/page.tsx index 254f9c60..f86b2291 100644 --- a/src/app/dev/investasi/create/investasi/page.tsx +++ b/src/app/dev/investasi/create/investasi/page.tsx @@ -1,19 +1,8 @@ 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(); - return ( <> - {/* */} ); diff --git a/src/app/lib/global_state.ts b/src/app/lib/global_state.ts index e49b6643..b26559c5 100644 --- a/src/app/lib/global_state.ts +++ b/src/app/lib/global_state.ts @@ -59,3 +59,7 @@ export const gs_votingTiggerBeranda = atom(false); // donasi export const gs_adminDonasi_triggerReview = atom(false); export const gs_donasiTriggerBeranda = atom(false); + +// investasi +export const gs_adminInvestasi_triggerReview = atom(false); +export const gs_investasiTriggerBeranda = atom(false); \ No newline at end of file diff --git a/src/app/lib/realtime_provider.tsx b/src/app/lib/realtime_provider.tsx index 3e25986b..6ae944d7 100644 --- a/src/app/lib/realtime_provider.tsx +++ b/src/app/lib/realtime_provider.tsx @@ -7,10 +7,12 @@ import { gs_admin_ntf, gs_adminDonasi_triggerReview, gs_adminEvent_triggerReview, + gs_adminInvestasi_triggerReview, gs_adminJob_triggerReview, gs_adminVoting_triggerReview, gs_donasiTriggerBeranda, gs_eventTriggerBeranda, + gs_investasiTriggerBeranda, gs_jobTiggerBeranda, gs_realtimeData, gs_user_ntf, @@ -71,6 +73,14 @@ export default function RealtimeProvider({ gs_donasiTriggerBeranda ); + // INVESTASI + const [isAdminInvestasi_TriggerReview, setIsAdminInvestasi_TriggerReview] = + useAtom(gs_adminInvestasi_triggerReview); + + const [isTriggerInvestasiBeranda, setIsTriggerInvestasiBeranda] = useAtom( + gs_investasiTriggerBeranda + ); + useShallowEffect(() => { try { WibuRealtime.init({ @@ -195,12 +205,32 @@ export default function RealtimeProvider({ // ) { // } - // ---------------------- DONASI ------------------------- // + + // ---------------------- INVESTASI ------------------------- // + + if ( + data.type == "trigger" && + data.pushNotificationTo == "ADMIN" && + data.dataMessage?.kategoriApp == "INVESTASI" + ) { + setIsAdminInvestasi_TriggerReview(true); + } + + if ( + data.type == "trigger" && + data.pushNotificationTo == "USER" && + data.dataMessage?.kategoriApp == "INVESTASI" && + data.dataMessage.status == "Publish" + ) { + setIsTriggerInvestasiBeranda(true); + } + + // ---------------------- INVESTASI ------------------------- // }, }); } catch (error) { - console.log("Error!:", error); + console.log("Error Realtime:", error); } }, []); diff --git a/src/app_modules/_global/fun/index.ts b/src/app_modules/_global/fun/index.ts index 809afd9b..2a0bb482 100644 --- a/src/app_modules/_global/fun/index.ts +++ b/src/app_modules/_global/fun/index.ts @@ -1,5 +1,7 @@ import { funGlobal_DeleteFileById } from "./delete/fun_delete_file_by_id"; import { funGlobal_UploadToStorage } from "./upload/fun_upload_to_storage"; +import { funValidasiUploadCreatedFile } from "./upload/fun_validasi_upload_created_file"; export { funGlobal_UploadToStorage }; export { funGlobal_DeleteFileById }; +export { funValidasiUploadCreatedFile }; diff --git a/src/app_modules/_global/fun/upload/fun_validasi_upload_created_file.ts b/src/app_modules/_global/fun/upload/fun_validasi_upload_created_file.ts new file mode 100644 index 00000000..228fd510 --- /dev/null +++ b/src/app_modules/_global/fun/upload/fun_validasi_upload_created_file.ts @@ -0,0 +1,106 @@ +import { clientLogger } from "@/util/clientLogger"; +import { MAX_SIZE } from "../../lib"; +import { PemberitahuanMaksimalFile } from "../../lib/max_size"; +import { ComponentGlobal_NotifikasiPeringatan } from "../../notif_global"; +import { funGlobal_DeleteFileById } from "../delete/fun_delete_file_by_id"; +import { funGlobal_UploadToStorage } from "./fun_upload_to_storage"; + +export async function funValidasiUploadCreatedFile({ + files, + dirId, + fileId, + onSetFileId, + onSetImageBuffer, +}: { + files: any | null; + dirId: string; + fileId: string; + onSetFileId: (val: string) => void; + onSetImageBuffer: (val: any | null) => void; +}) { + try { + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + + if (files.size > MAX_SIZE) { + ComponentGlobal_NotifikasiPeringatan(PemberitahuanMaksimalFile); + onSetImageBuffer(null); + + return false; + } + + if (fileId != "") { + const deleteFotoProfile = await funGlobal_DeleteFileById({ + fileId: fileId, + dirId: dirId, + }); + + if (!deleteFotoProfile.success) { + console.log( + `Client failed delete ${dirId}:` + deleteFotoProfile.message + ); + onSetImageBuffer(null); + + return false; + } + + if (deleteFotoProfile.success) { + onSetFileId(""); + onSetImageBuffer(null); + + const uploadPhoto = await funGlobal_UploadToStorage({ + file: files, + dirId: dirId, + }); + + if (!uploadPhoto.success) { + clientLogger.error( + `Client failed upload ${dirId}:` + uploadPhoto.message + ); + return false; + } + + if (uploadPhoto.success) { + clientLogger.info(`Client success upload ${dirId}`); + onSetFileId(""); + onSetImageBuffer(buffer); + + return true; + } else { + clientLogger.error("Client failed upload foto:", uploadPhoto.message); + ComponentGlobal_NotifikasiPeringatan(`Gagal upload ${dirId}`); + } + } + } else { + const uploadPhoto = await funGlobal_UploadToStorage({ + file: files, + dirId: dirId, + }); + + if (!uploadPhoto.success) { + clientLogger.error( + `Client failed upload ${dirId}:` + uploadPhoto.message + ); + return false; + } + + if (uploadPhoto.success) { + clientLogger.info(`Client success upload ${dirId}`); + onSetFileId(""); + onSetImageBuffer(buffer); + + return true; + } else { + clientLogger.error("Client failed upload foto:", uploadPhoto.message); + ComponentGlobal_NotifikasiPeringatan(`Gagal upload ${dirId}`); + + return false; + } + } + } catch (error) { + console.log(error); + + return false; + } +} diff --git a/src/app_modules/admin/investasi/detail/detail_review.tsx b/src/app_modules/admin/investasi/detail/detail_review.tsx index 7db97675..30d613f4 100644 --- a/src/app_modules/admin/investasi/detail/detail_review.tsx +++ b/src/app_modules/admin/investasi/detail/detail_review.tsx @@ -1,18 +1,14 @@ "use client"; +import { IRealtimeData } from "@/app/lib/global_state"; import { MODEL_INVESTASI } from "@/app_modules/investasi/_lib/interface"; import getOneInvestasiById from "@/app_modules/investasi/fun/get_one_investasi_by_id"; -import mqtt_client from "@/util/mqtt_client"; -import { - Button, - Group, - SimpleGrid, - Stack -} from "@mantine/core"; +import { Button, Group, SimpleGrid, Stack } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import _ from "lodash"; import { useRouter } from "next/navigation"; import { useState } from "react"; +import { WibuRealtime } from "wibu-pkg"; import { Admin_ComponentModalReport } from "../../_admin_global/_component"; import { ComponentAdminGlobal_NotifikasiBerhasil } from "../../_admin_global/admin_notifikasi/notifikasi_berhasil"; import { ComponentAdminGlobal_NotifikasiGagal } from "../../_admin_global/admin_notifikasi/notifikasi_gagal"; @@ -60,24 +56,25 @@ export default function AdminInvestasi_DetailReview({ if (res.status === 200) { setIsLoadingReject(true); - const dataNotif = { - appId: res.data?.id, - userId: res.data?.authorId, - pesan: res.data?.title, - status: res.data?.MasterStatusInvestasi?.name, + const dataNotifikasi: IRealtimeData = { + appId: res.data?.id as string, + userId: res.data?.authorId as string, + pesan: res.data?.title as string, + status: res.data?.MasterStatusInvestasi?.name as any, kategoriApp: "INVESTASI", title: "Investasi anda di tolak !", }; const notif = await adminNotifikasi_funCreateToUser({ - data: dataNotif as any, + data: dataNotifikasi as any, }); if (notif.status === 201) { - mqtt_client.publish( - "USER", - JSON.stringify({ userId: res?.data?.authorId, count: 1 }) - ); + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "USER", + dataMessage: dataNotifikasi, + }); } const loadData = await getOneInvestasiById(data.id); @@ -100,8 +97,8 @@ export default function AdminInvestasi_DetailReview({ progesInvestasiId: "1", }); if (res.status === 200) { - const dataNotif = { - appId: res.data?.id, + const dataNotifikasi: IRealtimeData = { + appId: res.data?.id as string, userId: res.data?.authorId as any, pesan: res.data?.title as any, status: res.data?.MasterStatusInvestasi?.name as any, @@ -110,19 +107,21 @@ export default function AdminInvestasi_DetailReview({ }; const notif = await adminNotifikasi_funCreateToUser({ - data: dataNotif as any, + data: dataNotifikasi as any, }); if (notif.status === 201) { - mqtt_client.publish( - "USER", - JSON.stringify({ userId: res?.data?.authorId, count: 1 }) - ); + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "USER", + dataMessage: dataNotifikasi, + }); - mqtt_client.publish( - "Beranda_Investasi", - JSON.stringify({ update: true }) - ); + WibuRealtime.setData({ + type: "trigger", + pushNotificationTo: "USER", + dataMessage: dataNotifikasi, + }); const loadData = await getOneInvestasiById(data.id); setData(loadData as any); @@ -216,8 +215,6 @@ export default function AdminInvestasi_DetailReview({ } /> - - ); } diff --git a/src/app_modules/admin/investasi/main/table_review.tsx b/src/app_modules/admin/investasi/main/table_review.tsx index b8e45c46..432241e4 100644 --- a/src/app_modules/admin/investasi/main/table_review.tsx +++ b/src/app_modules/admin/investasi/main/table_review.tsx @@ -1,33 +1,33 @@ "use client"; -import { RouterAdminInvestasi } from "@/app/lib/router_admin/router_admin_investasi"; +import { gs_adminInvestasi_triggerReview } from "@/app/lib/global_state"; import { RouterAdminInvestasi_OLD } from "@/app/lib/router_hipmi/router_admin"; +import { AccentColor } from "@/app_modules/_global/color"; import { MODEL_INVESTASI } from "@/app_modules/investasi/_lib/interface"; import { - Badge, - ActionIcon, - Box, - ScrollArea, - Table, - Tooltip, - Stack, - Group, - Avatar, - Text, - Center, + Affix, Button, + Center, + Group, Pagination, Paper, + rem, + ScrollArea, + Stack, + Table, + Text, TextInput, Title, } from "@mantine/core"; -import { IconChevronLeft, IconEdit, IconSearch } from "@tabler/icons-react"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconRefresh, IconSearch } from "@tabler/icons-react"; +import { useAtom } from "jotai"; +import _ from "lodash"; import { useRouter } from "next/navigation"; import { useState } from "react"; import ComponentAdminGlobal_HeaderTamplate from "../../_admin_global/header_tamplate"; -import { adminInvestasi_funGetAllReview } from "../fun/get/get_all_review"; -import _ from "lodash"; import ComponentAdminGlobal_IsEmptyData from "../../_admin_global/is_empty_data"; import ComponentAdminGlobal_TampilanRupiahDonasi from "../../_admin_global/tampilan_rupiah"; +import { adminInvestasi_funGetAllReview } from "../fun/get/get_all_review"; export default function Admin_TableReviewInvestasi({ dataInvestsi, @@ -53,6 +53,33 @@ function TableView({ listData }: { listData: any }) { const [isLoading, setLoading] = useState(false); const [idData, setIdData] = useState(""); + // Realtime + const [isAdminInvestasi_TriggerReview, setIsAdminInvestasi_TriggerReview] = + useAtom(gs_adminInvestasi_triggerReview); + const [isShowReload, setIsShowReload] = useState(false); + const [isLoadingReload, setLoadingReload] = useState(false); + + useShallowEffect(() => { + if (isAdminInvestasi_TriggerReview) { + setIsShowReload(false); + } + }, [isAdminInvestasi_TriggerReview]); + + useShallowEffect(() => { + if (isAdminInvestasi_TriggerReview) { + setIsShowReload(true); + } + }, [isAdminInvestasi_TriggerReview]); + + async function onLoadData() { + const loadData = await adminInvestasi_funGetAllReview({ page: 1 }); + setData(loadData.data as any); + setNPage(loadData.nPage); + setLoadingReload(false); + setIsShowReload(false); + setIsAdminInvestasi_TriggerReview(false); + } + async function onSearch(s: string) { setSearch(s); setActivePage(1); @@ -151,6 +178,30 @@ function TableView({ listData }: { listData: any }) { ) : ( + {isShowReload && ( + + +
+ +
+
+
+ )} + void; - onToggleNavbar: (val: any) => void; + onLoadCountNotif: (val: any) => void; + onLoadDataNotifikasi: (val: any) => void; + onChangeNavbar: (val: { + id: IAdmin_ActivePage; + childId: IAdmin_ActiveChildId; + }) => void; }) { - if (data.status === "Review") { - const path = RouterAdminInvestasi.table_review; - router.push(path); - onChangeNavbar({ - id: 2, - childId: 23, + const check = await admin_funInvestasiCheckStatus({ id: appId }); + + if (check.status == 200) { + const udpateReadNotifikasi = await adminNotifikasi_funUpdateIsReadById({ + notifId: notifikasiId, }); + + if (udpateReadNotifikasi.status == 200) { + const loadCountNotif = await adminNotifikasi_countNotifikasi(); + onLoadCountNotif(loadCountNotif); + + const loadListNotifikasi = await adminNotifikasi_getByUserId({ + page: 1, + }); + onLoadDataNotifikasi(loadListNotifikasi); + + const path = `/dev/admin/investasi/sub-menu/${check.statusName}`; + + if (check.statusName == "draft") { + ComponentAdminGlobal_NotifikasiPeringatan( + "Status telah dirubah oleh user" + ); + } else { + if (check.statusName == "publish") { + onChangeNavbar({ + id: "Investasi", + childId: "Investasi_2", + }); + } + + if (check.statusName == "review") { + onChangeNavbar({ + id: "Investasi", + childId: "Investasi_3", + }); + } + + if (check.statusName == "reject") { + onChangeNavbar({ + id: "Investasi", + childId: "Investasi_4", + }); + } + + router.push(path, { scroll: false }); + } + + return true; + } + } else { + ComponentAdminGlobal_NotifikasiPeringatan("Status tidak ditemukan"); + return false; } + // if ( + // status == "Menunggu" || + // status == "Berhasil" || + // status == "Proses" || + // status == "Gagal" + // ) { + // const path = RouterAdminDonasi_OLD.detail_publish + appId; + // router.push(path, { scroll: false }); + // onChangeNavbar({ + // id: "Donasi", + // childId: "Donasi_2", + // }); - // if ( - // data.status === "Menunggu" || - // data.status === "Berhasil" || - // data.status === "Proses" || - // data.status === "Gagal" - // ) { - // const path = RouterAdminDonasi_OLD.detail_publish + data.appId; - // router.push(path, { scroll: false }); - // onChangeNavbar({ - // id: 3, - // childId: 32, - // }); - // } + // return true; + // } else { - onToggleNavbar(true); + // } } diff --git a/src/app_modules/admin/notifikasi/view_card_drawer.tsx b/src/app_modules/admin/notifikasi/view_card_drawer.tsx index 37120d27..57484dc9 100644 --- a/src/app_modules/admin/notifikasi/view_card_drawer.tsx +++ b/src/app_modules/admin/notifikasi/view_card_drawer.tsx @@ -24,6 +24,7 @@ import { } from "./route_setting/type_of_select_page"; import { adminNotifikasi_findRouterVoting } from "./route_setting/voting"; import adminNotifikasi_findRouterDonasi from "./route_setting/donasi"; +import adminNotifikasi_findRouterInvestasi from "./route_setting/investasi"; export default function AdminNotifikasi_ViewCardDrawer({ data, @@ -166,6 +167,9 @@ export default function AdminNotifikasi_ViewCardDrawer({ // ========================== DONASI ========================== // if (data.kategoriApp == "DONASI") { + setVisible(true); + setDataId(data.id); + const checkDonasi = await adminNotifikasi_findRouterDonasi({ appId: data.appId, notifikasiId: data.id, @@ -195,6 +199,39 @@ export default function AdminNotifikasi_ViewCardDrawer({ // ========================== DONASI ========================== // + // ========================== INVESTASI ========================== // + + if (data.kategoriApp == "INVESTASI") { + setVisible(true); + setDataId(data.id); + + const checkInvestasi = await adminNotifikasi_findRouterInvestasi({ + appId: data.appId, + notifikasiId: data.id, + status: data.status as ITypeStatusNotifikasi, + router: router, + onLoadCountNotif(val) { + onLoadCountNotif(val); + }, + onLoadDataNotifikasi(val) { + onLoadDataNotifikasi(val); + }, + onChangeNavbar(val) { + onChangeNavbar({ + id: val.id, + childId: val.childId, + }); + }, + }); + + if (checkInvestasi) { + setIsAdminDonasi_TriggerReview(false); + setVisible(false); + setDataId(""); + onToggleNavbar(false); + } + } + // // FORUM // e?.kategoriApp === "FORUM" && // adminNotifikasi_findRouterForum({ @@ -220,36 +257,6 @@ export default function AdminNotifikasi_ViewCardDrawer({ // onToggleNavbar(val); // }, // }); - - // // EVENT - // e?.kategoriApp === "EVENT" && - // - - // // DONASI - // e.kategoriApp === "DONASI" && - // adminNotifikasi_findRouterDonasi({ - // data: e, - // router: router, - // onChangeNavbar(val) { - // onChangeNavbar(val); - // }, - // onToggleNavbar(val) { - // onToggleNavbar(val); - // }, - // }); - - // // INVESTASI - // e.kategoriApp === "INVESTASI" && - // adminNotifikasi_findRouterInvestasi({ - // data: e, - // router: router, - // onChangeNavbar(val) { - // onChangeNavbar(val); - // }, - // onToggleNavbar(val) { - // onToggleNavbar(val); - // }, - // }); } return ( diff --git a/src/app_modules/home/view_home_new.tsx b/src/app_modules/home/view_home_new.tsx index b6f4dd51..bd5cc8cc 100644 --- a/src/app_modules/home/view_home_new.tsx +++ b/src/app_modules/home/view_home_new.tsx @@ -70,12 +70,12 @@ export default function HomeViewNew() { variant={"transparent"} onClick={() => { if ( - dataUser.profile === undefined || - dataUser?.profile === null + dataUser.profile != undefined || + dataUser?.profile != null ) { - router.push(RouterProfile.create, { scroll: false }); - } else { router.push(RouterUserSearch.main, { scroll: false }); + } else { + router.push(RouterProfile.create, { scroll: false }); } }} > @@ -88,11 +88,9 @@ export default function HomeViewNew() { disabled={countNtf == null} onClick={() => { if ( - dataUser.profile === undefined || - dataUser?.profile === null + dataUser.profile != undefined || + dataUser?.profile != null ) { - router.push(RouterProfile.create, { scroll: false }); - } else { setCategoryPage("Semua"); router.push( RouterNotifikasi.categoryApp({ name: "semua" }), @@ -100,6 +98,8 @@ export default function HomeViewNew() { scroll: false, } ); + } else { + router.push(RouterProfile.create, { scroll: false }); } }} > diff --git a/src/app_modules/investasi/_component/button/comp_button_create_new_investasi.tsx b/src/app_modules/investasi/_component/button/comp_button_create_new_investasi.tsx index f7c09f94..8dbb877b 100644 --- a/src/app_modules/investasi/_component/button/comp_button_create_new_investasi.tsx +++ b/src/app_modules/investasi/_component/button/comp_button_create_new_investasi.tsx @@ -10,13 +10,15 @@ import { ComponentGlobal_NotifikasiPeringatan, } from "@/app_modules/_global/notif_global"; import { notifikasiToAdmin_funCreate } from "@/app_modules/notifikasi/fun"; -import mqtt_client from "@/util/mqtt_client"; import { Button } from "@mantine/core"; import { useAtom } from "jotai"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { investasi_funCreateNewInvestasi } from "../../_fun"; import { gs_investas_menu, gs_investasi_status } from "../../g_state"; +import { clientLogger } from "@/util/clientLogger"; +import { IRealtimeData } from "@/app/lib/global_state"; +import { WibuRealtime } from "wibu-pkg"; export function Investasi_ComponentButtonCreateNewInvestasi({ data, @@ -34,74 +36,84 @@ export function Investasi_ComponentButtonCreateNewInvestasi({ const [activeTab, setActiveTab] = useAtom(gs_investasi_status); const [hotMenu, setHotMenu] = useAtom(gs_investas_menu); - async function onSubmit() { - setIsLoading(true); - const body = { - title: data.title, - targetDana: data.targetDana, - hargaLembar: data.hargaLembar, - totalLembar: totalLembar, - roi: data.roi, - masterPeriodeDevidenId: data.periodeDevidenId, - masterPembagianDevidenId: data.pembagianDevidenId, - masterPencarianInvestorId: data.pencarianInvestorId, - }; - - const uploadImage = await funGlobal_UploadToStorage({ - file: fileImage, - dirId: DIRECTORY_ID.investasi_image, - }); - if (!uploadImage.success) { - setIsLoading(false); - return ComponentGlobal_NotifikasiPeringatan("Gagal upload file gambar"); - } - - const uploadFilePdf = await funGlobal_UploadToStorage({ - file: filePdf, - dirId: DIRECTORY_ID.investasi_prospektus, - }); - if (!uploadFilePdf.success) { - setIsLoading(false); - return ComponentGlobal_NotifikasiPeringatan("Gagal upload file pdf"); - } - - const res = await investasi_funCreateNewInvestasi({ - data: body as any, - fileImageId: uploadImage.data.id, - filePdfId: uploadFilePdf.data.id, - }); - - if (res.status === 201) { - const dataNotif = { - appId: res.data?.id, - status: res.data?.MasterStatusInvestasi?.name, - userId: res.data?.authorId, - pesan: res.data?.title, - kategoriApp: "INVESTASI", - title: "Investasi baru", + async function onCreate() { + try { + setIsLoading(true); + const body = { + title: data.title, + targetDana: data.targetDana, + hargaLembar: data.hargaLembar, + totalLembar: totalLembar, + roi: data.roi, + masterPeriodeDevidenId: data.periodeDevidenId, + masterPembagianDevidenId: data.pembagianDevidenId, + masterPencarianInvestorId: data.pencarianInvestorId, }; - const notif = await notifikasiToAdmin_funCreate({ - data: dataNotif as any, + const uploadImage = await funGlobal_UploadToStorage({ + file: fileImage, + dirId: DIRECTORY_ID.investasi_image, + }); + if (!uploadImage.success) { + setIsLoading(false); + ComponentGlobal_NotifikasiPeringatan("Gagal upload file gambar"); + return; + } + + const uploadFilePdf = await funGlobal_UploadToStorage({ + file: filePdf, + dirId: DIRECTORY_ID.investasi_prospektus, + }); + if (!uploadFilePdf.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal upload file pdf"); + return; + } + + const res = await investasi_funCreateNewInvestasi({ + data: body as any, + fileImageId: uploadImage.data.id, + filePdfId: uploadFilePdf.data.id, }); - if (notif.status === 201) { - mqtt_client.publish( - "ADMIN", - JSON.stringify({ - count: 1, - }) - ); - setActiveTab("Review"); - setHotMenu(1); - setIsLoading(true); - ComponentGlobal_NotifikasiBerhasil(res.message); - router.push(NEW_RouterInvestasi.portofolio({ id: "2" })); + if (res.status === 201) { + const dataNotifikasi: IRealtimeData = { + appId: res.data?.id as string, + status: res.data?.MasterStatusInvestasi?.name as any, + userId: res.data?.authorId as string, + pesan: res.data?.title as string, + kategoriApp: "INVESTASI", + title: "Investasi baru", + }; + + const notif = await notifikasiToAdmin_funCreate({ + data: dataNotifikasi as any, + }); + + if (notif.status === 201) { + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "ADMIN", + }); + + WibuRealtime.setData({ + type: "trigger", + pushNotificationTo: "ADMIN", + dataMessage: dataNotifikasi, + }); + + router.push(NEW_RouterInvestasi.portofolio({ id: "2" })); + setActiveTab("Review"); + setHotMenu(1); + setIsLoading(true); + ComponentGlobal_NotifikasiBerhasil(res.message); + } + } else { + ComponentGlobal_NotifikasiGagal(res.message); } + } catch (error) { + clientLogger.error("Error create new investasi", error); + } finally { setIsLoading(false); - } else { - setIsLoading(false); - ComponentGlobal_NotifikasiGagal(res.message); } } @@ -131,7 +143,7 @@ export function Investasi_ComponentButtonCreateNewInvestasi({ bg={MainColor.yellow} color="yellow" c={"black"} - onClick={() => onSubmit()} + onClick={() => onCreate()} > Simpan diff --git a/src/app_modules/investasi/_component/main/comp_update_beranda.tsx b/src/app_modules/investasi/_component/main/comp_update_beranda.tsx index adb5a168..71d014e6 100644 --- a/src/app_modules/investasi/_component/main/comp_update_beranda.tsx +++ b/src/app_modules/investasi/_component/main/comp_update_beranda.tsx @@ -3,29 +3,27 @@ import { Affix, Button, Center, rem } from "@mantine/core"; import { useState } from "react"; import { investasi_funGetAllPublish } from "../../fun/get_all_investasi"; import { data } from "autoprefixer"; +import { apiGetAllInvestasi } from "../../_lib/api_interface"; export function Investasi_ComponentButtonUpdateBeranda({ onLoadData, }: { onLoadData: (val: any) => void; }) { - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setLoading] = useState(false); async function onLoaded() { try { - await investasi_funGetAllPublish({ page: 1 }); + setLoading(true); + const response = await apiGetAllInvestasi(`?cat=bursa&page=1`); + if (response.success) { + onLoadData(response.data); + } } catch (error) { - console.log(error); + console.error(error); } finally { - const loadData = await investasi_funGetAllPublish({ page: 1 }); - - onLoadData({ - data: loadData, - isNewPost: false, - }); + setLoading(false); } - - setIsLoading(true); } return ( diff --git a/src/app_modules/investasi/_view/main/view_beranda_new.tsx b/src/app_modules/investasi/_view/main/view_beranda_new.tsx index 4ee233a5..0090d284 100644 --- a/src/app_modules/investasi/_view/main/view_beranda_new.tsx +++ b/src/app_modules/investasi/_view/main/view_beranda_new.tsx @@ -1,4 +1,4 @@ -'use client' +"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"; @@ -14,84 +14,87 @@ import { Investasi_ComponentCardBerandaNew } from "../../_component/main/com_car import { apiGetAllInvestasi } from "../../_lib/api_interface"; import { IDataInvestasiBursa } from "../../_lib/type_investasi"; import SkeletonInvestasiBursa from "./skeleton_beranda"; +import { useAtom } from "jotai"; +import { gs_investasiTriggerBeranda } from "@/app/lib/global_state"; export function Investasi_ViewBerandaNew() { - const [data, setData] = useState([]); - const [activePage, setActivePage] = useState(1); - const [isNewPost, setIsNewPost] = useState(false); - const [loading, setLoading] = useState(true) + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true); - useShallowEffect(() => { - mqtt_client.subscribe("Beranda_Investasi"); + // Realtime + const [isShowUpdate, setIsShowUpdate] = useState(false); + const [isTriggerReload, setIsTriggerReload] = useAtom( + gs_investasiTriggerBeranda + ); - mqtt_client.on("message", (topic, message) => { - const newPost = JSON.parse(message.toString()); - setIsNewPost(newPost); - }); - }, []); + useShallowEffect(() => { + if (isTriggerReload) { + setIsShowUpdate(true); + } + }, [isTriggerReload]); - - 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) + 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(() => { + setIsTriggerReload(false); + setIsShowUpdate(false); + getDataInvestasi(); + }, []); - useShallowEffect(() => { - getDataInvestasi() - }, []); + return ( + <> + {isShowUpdate && ( + { + setData(val); + setIsShowUpdate(false); + }} + /> + )} - 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); - - - { - 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) => } -
- ) - - } -
- - ); + return loadData.data as any; + }} + > + {(item) => } +
+ )} +
+ + ); } diff --git a/src/app_modules/investasi/create/view_new.tsx b/src/app_modules/investasi/create/view_new.tsx index 8eb154db..98ae33be 100644 --- a/src/app_modules/investasi/create/view_new.tsx +++ b/src/app_modules/investasi/create/view_new.tsx @@ -1,413 +1,485 @@ "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 { + ComponentGlobal_BoxInformation, + ComponentGlobal_BoxUploadImage, + ComponentGlobal_CardStyles, +} from "@/app_modules/_global/component"; +import { MAX_SIZE } from "@/app_modules/_global/lib"; +import { PemberitahuanMaksimalFile } from "@/app_modules/_global/lib/max_size"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { + AspectRatio, + Box, + Button, + Center, + FileButton, + Grid, + Group, + Image, + Loader, + Select, + Stack, + Text, + TextInput, +} from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; -import { IconCamera, IconCircleCheck, IconFileTypePdf, IconUpload } from "@tabler/icons-react"; +import { + IconCamera, + IconCircleCheck, + IconFileTypePdf, + IconPhoto, +} 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(""); + 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)); + const [isLoadingImg, setIsLoadingImg] = useState(false); + const [isLoadingPdf, setIsLoadingPdf] = useState(false); + + 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 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 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(); + }, []); - 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) - } - } + return ( + <> + + {/* Upload Image */} + + + + + + {isLoadingImg ? ( + + + + ) : img ? ( + + + + ) : ( + + + + )} + - useShallowEffect(() => { - onGetMasterInvestor() - onGetMasterPeriodeDeviden() - onGetMasterPembagianDeviden() - }, []) + {/* Upload Foto */} + + { + try { + setIsLoadingImg(true); + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); - return ( - <> - - {/* Upload Image */} - - - - - - {img ? ( - - - - ) : ( - - - - Upload Gambar - - - )} - + if (files.size > MAX_SIZE) { + ComponentGlobal_NotifikasiPeringatan( + PemberitahuanMaksimalFile + ); + setImg(null); - {/* 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); - } - }} - /> - - - - - % - + return; } - 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, - }); - }} - /> + {/* Upload File */} + + + + {isLoadingPdf ? ( + + + + ) : !filePdf ? ( + + + + ) : ( + + + + + {filePdf.name} + + + +
+ +
+
+
+ )} +
- ({ + value: e.id, + label: e.name + " " + "hari", + }))} + onChange={(val) => { + setValue({ + ...(value as any), + pencarianInvestorId: val, + }); + }} + /> + + ({ + value: e.id, + label: e.name + " " + "bulan", + }))} + onChange={(val) => { + setValue({ + ...(value as any), + pembagianDevidenId: val, + }); + }} + /> +
+ + +
+ + ); } diff --git a/src/app_modules/job/create/view.tsx b/src/app_modules/job/create/view.tsx index 9b836c0f..7a84194f 100644 --- a/src/app_modules/job/create/view.tsx +++ b/src/app_modules/job/create/view.tsx @@ -34,6 +34,9 @@ import { } from "@/app_modules/_global/component"; import { Job_ComponentButtonSaveCreate } from "../component"; import { defaultDeskripsi, defaultSyarat } from "../component/default_value"; +import { MAX_SIZE } from "@/app_modules/_global/lib"; +import { PemberitahuanMaksimalFile } from "@/app_modules/_global/lib/max_size"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; export default function Job_Create() { const [value, setValue] = useState({ @@ -80,6 +83,16 @@ export default function Job_Create() { const buffer = URL.createObjectURL( new Blob([new Uint8Array(await files.arrayBuffer())]) ); + + if (files.size > MAX_SIZE) { + ComponentGlobal_NotifikasiPeringatan( + PemberitahuanMaksimalFile + ); + setImg(null); + + return; + } + setImg(buffer); setFile(files); } catch (error) { diff --git a/src/app_modules/notifikasi/component/card_view.tsx b/src/app_modules/notifikasi/component/card_view.tsx index c6171e9a..caaa9c00 100644 --- a/src/app_modules/notifikasi/component/card_view.tsx +++ b/src/app_modules/notifikasi/component/card_view.tsx @@ -6,22 +6,24 @@ import { MainColor, } from "@/app_modules/_global/color/color_pallet"; import { ComponentGlobal_CardLoadingOverlay } from "@/app_modules/_global/component"; +import { gs_donasi_hot_menu } from "@/app_modules/donasi/global_state"; import { gs_event_hotMenu } from "@/app_modules/event/global_state"; +import { gs_investas_menu } from "@/app_modules/investasi/g_state"; import { gs_job_hot_menu } from "@/app_modules/job/global_state"; +import { gs_vote_hotMenu } from "@/app_modules/vote/global_state"; import { Badge, Card, Divider, Group, Stack, Text } from "@mantine/core"; import { IconCheck, IconChecks } from "@tabler/icons-react"; import { useAtom } from "jotai"; -import { useRouter } from "next/navigation"; -import { useState } from "react"; -import { ICategoryapp, MODEL_NOTIFIKASI } from "../model/interface"; -import { notifikasi_eventCheckStatus } from "./path/event"; -import { notifikasi_jobCheckStatus } from "./path/job"; -import { gs_vote_hotMenu } from "@/app_modules/vote/global_state"; -import { notifikasi_votingCheckStatus } from "./path/voting"; -import { redirectDonasiPage } from "./path/donasi"; -import { gs_donasi_hot_menu } from "@/app_modules/donasi/global_state"; import moment from "moment"; import "moment/locale/id"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { MODEL_NOTIFIKASI } from "../model/interface"; +import { redirectDonasiPage } from "./path/donasi"; +import { notifikasi_eventCheckStatus } from "./path/event"; +import { redirectInvestasiPage } from "./path/investasi"; +import { notifikasi_jobCheckStatus } from "./path/job"; +import { notifikasi_votingCheckStatus } from "./path/voting"; export function ComponentNotifiaksi_CardView({ data, @@ -40,6 +42,7 @@ export function ComponentNotifiaksi_CardView({ const [eventMenuId, setEventMenuId] = useAtom(gs_event_hotMenu); const [votingMenu, setVotingMenu] = useAtom(gs_vote_hotMenu); const [donasiMenu, setDonasiMenu] = useAtom(gs_donasi_hot_menu); + const [investasiMenu, setInvestasiMenu] = useAtom(gs_investas_menu); return ( <> @@ -76,6 +79,8 @@ export function ComponentNotifiaksi_CardView({ setLoadCountNtf(val); }, }); + + return; } // EVENT @@ -98,6 +103,8 @@ export function ComponentNotifiaksi_CardView({ setLoadCountNtf(val); }, }); + + return; } if (data?.kategoriApp === "VOTING") { @@ -119,6 +126,8 @@ export function ComponentNotifiaksi_CardView({ setLoadCountNtf(val); }, }); + + return; } if (data?.kategoriApp === "DONASI") { @@ -140,6 +149,31 @@ export function ComponentNotifiaksi_CardView({ setLoadCountNtf(val); }, }); + + return; + } + + if (data?.kategoriApp === "INVESTASI") { + redirectInvestasiPage({ + appId: data.appId, + dataId: data.id, + categoryPage: categoryPage, + router: router, + onLoadDataEvent(val) { + onLoadData(val); + }, + onSetVisible(val) { + setVisible(val); + }, + onSetMenuId(val) { + setInvestasiMenu(val); + }, + onLoadCountNtf(val) { + setLoadCountNtf(val); + }, + }); + + return; } // data?.kategoriApp === "FORUM" && @@ -148,38 +182,11 @@ export function ComponentNotifiaksi_CardView({ // router: router, // }); - // data?.kategoriApp === "VOTING" && - // redirectVotingPage({ - // data: data, - // router: router, - // onSetPage(val) { - // // onSetMenu(val); - // }, - // }); - - // data?.kategoriApp === "EVENT" && - // redirectEventPage({ - // data: data, - // router: router, - // onSetPage(val) { - // // onSetMenu(val); - // }, - // }); - // data?.kategoriApp === "COLLABORATION" && // redirectDetailCollaborationPage({ // data: data, // router: router, // }); - - // data.kategoriApp === "INVESTASI" && - // redirectInvestasiPage({ - // data: data, - // router: router, - // onSetPage(val) { - // // onSetMenu(val); - // }, - // }); }} > {/*
{JSON.stringify(e, null, 2)}
*/} diff --git a/src/app_modules/notifikasi/component/path/investasi.ts b/src/app_modules/notifikasi/component/path/investasi.ts index 82be9827..6a51e236 100644 --- a/src/app_modules/notifikasi/component/path/investasi.ts +++ b/src/app_modules/notifikasi/component/path/investasi.ts @@ -1,37 +1,61 @@ import { RouterEvent } from "@/app/lib/router_hipmi/router_event"; import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; import { MODEL_NOTIFIKASI } from "../../model/interface"; -import { RouterInvestasi_OLD } from "@/app/lib/router_hipmi/router_investasi"; +import { RouterDonasi } from "@/app/lib/router_hipmi/router_donasi"; +import { notifikasi_funDonasiCheckStatus } from "../../fun/check/fun_check_donasi_status"; +import notifikasi_getByUserId from "../../fun/get/get_notifiaksi_by_id"; +import notifikasi_countUserNotifikasi from "../../fun/count/fun_count_by_id"; +import notifikasi_funUpdateIsReadById from "../../fun/update/fun_update_is_read_by_user_id"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { notifikasi_funInvestasiCheckStatus } from "../../fun/check/fun_check_investasi_status"; -export function redirectInvestasiPage({ - data, +export async function redirectInvestasiPage({ + appId, + dataId, + categoryPage, router, - onSetPage, + onLoadDataEvent, + onSetMenuId, + onSetVisible, + onLoadCountNtf, }: { - data: MODEL_NOTIFIKASI; + appId: string; + dataId: string; + categoryPage: string; router: AppRouterInstance; - onSetPage: (val: any) => void; + onLoadDataEvent: (val: any) => void; + onSetMenuId(val: number): void; + onSetVisible(val: boolean): void; + onLoadCountNtf(val: number): void; }) { - const path = RouterInvestasi_OLD.portofolio; + const check = await notifikasi_funInvestasiCheckStatus({ id: appId }); - if (data.status === "Publish") { - onSetPage({ - menuId: 2, - status: data.status, + if (check.status == 200) { + // const loadListNotifikasi = await notifikasi_getByUserId({ + // page: 1, + // kategoriApp: categoryPage as any, + // }); + // onLoadDataEvent(loadListNotifikasi); + + // const loadCountNotifikasi = await notifikasi_countUserNotifikasi(); + // onLoadCountNtf(loadCountNotifikasi); + + const updateReadNotifikasi = await notifikasi_funUpdateIsReadById({ + notifId: dataId, }); - router.push(path, { scroll: false }); - } - // console.log(data) - if (data.status === "Reject") { - onSetPage({ - menuId: 2, - status: data.status, - }); - router.push(path, { scroll: false }); - } + if (updateReadNotifikasi.status == 200) { + onSetVisible(true); - // if (data.status === "Peserta Event") { - // router.push(RouterEvent.detail_main + data.appId, { scroll: false }); - // } + onSetMenuId(1); + if (check.statusName == "publish") { + router.push(`/dev/investasi/detail/${appId}`, { scroll: false }); + } else { + const path = `/dev/investasi/detail/portofolio/${appId}`; + router.push(path, { scroll: false }); + } + } + } else { + ComponentGlobal_NotifikasiPeringatan("Status tidak ditemukan"); + } } diff --git a/src/app_modules/notifikasi/fun/check/fun_check_investasi_status.ts b/src/app_modules/notifikasi/fun/check/fun_check_investasi_status.ts new file mode 100644 index 00000000..a9e87485 --- /dev/null +++ b/src/app_modules/notifikasi/fun/check/fun_check_investasi_status.ts @@ -0,0 +1,31 @@ +"use server"; + +import { prisma } from "@/app/lib"; +import _ from "lodash"; + +export async function notifikasi_funInvestasiCheckStatus({ + id, +}: { + id: string; +}) { + const data = await prisma.investasi.findUnique({ + where: { + id: id, + }, + select: { + MasterStatusInvestasi: true, + }, + }); + + if (!data) + return { + status: 400, + message: "Investasi tidak ditemukan", + statusName: "", + }; + return { + status: 200, + message: "Berhasil di cek", + statusName: _.lowerCase(data.MasterStatusInvestasi?.name), + }; +} From 75ea1269b9717bfa5a2d21d6f0bcbc80b9a39f14 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 23 Dec 2024 07:51:55 +0800 Subject: [PATCH 087/595] chore(release): 1.2.33 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4d9e45d..d7d48516 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.33](https://github.com/bipproduction/hipmi/compare/v1.2.32...v1.2.33) (2024-12-22) + ## [1.2.32](https://github.com/bipproduction/hipmi/compare/v1.2.31...v1.2.32) (2024-12-19) diff --git a/package.json b/package.json index b7686848..35d83a3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.32", + "version": "1.2.33", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 0ca12c531848132986436a4c5119649a791c7fae Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 24 Dec 2024 16:48:09 +0800 Subject: [PATCH 088/595] Fix notifikasi Deskripsi: - Fix notifikasi investasi --- .../investasi/table_status/publish/page.tsx | 10 - .../investasi/table_status/reject/page.tsx | 10 - .../investasi/table_status/review/page.tsx | 9 - .../transaksi/metode-pembayaran/[id]/page.tsx | 1 - src/app/lib/global_state.ts | 3 +- src/app/zCoba/page.tsx | 4 - .../button/button_konfirmasi_transaksi.tsx | 138 +++++-- .../admin/investasi/detail/detail_publish.tsx | 1 - .../fun/edit/fun_accept_invoice_by_id.ts | 11 + .../fun/edit/fun_reject_invoice_by_id.ts | 14 +- .../admin/investasi/main/table_review.tsx | 7 +- .../admin/notifikasi/route_setting/donasi.ts | 46 ++- .../notifikasi/route_setting/investasi.ts | 122 ++++--- .../admin/notifikasi/view_card_drawer.tsx | 343 +++++++++--------- .../view_invoice_berhasil.tsx | 16 +- .../status_transaksi/view_transaksi_gagal.tsx | 7 +- .../_view/transaksi/view_invoice.tsx | 70 +++- .../transaksi/view_metode_pembayaran.tsx | 69 +++- .../notifikasi/component/path/investasi.ts | 40 +- .../fun/check/fun_check_investasi_status.ts | 1 + .../fun/check/fun_check_investor_status.ts | 32 ++ .../fun/create/create_notif_to_user.ts | 32 +- .../fun/get/fun_get_status_transaksi.ts | 22 ++ src/app_modules/notifikasi/fun/index.ts | 4 + 24 files changed, 618 insertions(+), 394 deletions(-) delete mode 100644 src/app/dev/admin/investasi/table_status/publish/page.tsx delete mode 100644 src/app/dev/admin/investasi/table_status/reject/page.tsx delete mode 100644 src/app/dev/admin/investasi/table_status/review/page.tsx create mode 100644 src/app_modules/notifikasi/fun/check/fun_check_investor_status.ts create mode 100644 src/app_modules/notifikasi/fun/get/fun_get_status_transaksi.ts diff --git a/src/app/dev/admin/investasi/table_status/publish/page.tsx b/src/app/dev/admin/investasi/table_status/publish/page.tsx deleted file mode 100644 index 1d9f989c..00000000 --- a/src/app/dev/admin/investasi/table_status/publish/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { Admin_TablePublishInvestasi } from "@/app_modules/admin/investasi"; -import Admin_funGetAllInvestasi from "@/app_modules/admin/investasi/fun/get_all_investasi"; - -export default async function Page() { - const listInvestasi = await Admin_funGetAllInvestasi(); - - return<> - - -} \ No newline at end of file diff --git a/src/app/dev/admin/investasi/table_status/reject/page.tsx b/src/app/dev/admin/investasi/table_status/reject/page.tsx deleted file mode 100644 index 55092f60..00000000 --- a/src/app/dev/admin/investasi/table_status/reject/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { Admin_TableRejectInvestasi } from "@/app_modules/admin/investasi"; -import Admin_funGetAllInvestasi from "@/app_modules/admin/investasi/fun/get_all_investasi"; - -export default async function Page() { - const dataInvestsi = await Admin_funGetAllInvestasi() - return <> - - - -} \ No newline at end of file diff --git a/src/app/dev/admin/investasi/table_status/review/page.tsx b/src/app/dev/admin/investasi/table_status/review/page.tsx deleted file mode 100644 index 350245f7..00000000 --- a/src/app/dev/admin/investasi/table_status/review/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { Admin_TableReviewInvestasi } from "@/app_modules/admin/investasi"; -import Admin_funGetAllInvestasi from "@/app_modules/admin/investasi/fun/get_all_investasi"; - -export default async function Page() { - const dataInvestsi = await Admin_funGetAllInvestasi() - return <> - - -} \ No newline at end of file diff --git a/src/app/dev/investasi/transaksi/metode-pembayaran/[id]/page.tsx b/src/app/dev/investasi/transaksi/metode-pembayaran/[id]/page.tsx index 7553f2ef..1c7e75a0 100644 --- a/src/app/dev/investasi/transaksi/metode-pembayaran/[id]/page.tsx +++ b/src/app/dev/investasi/transaksi/metode-pembayaran/[id]/page.tsx @@ -1,6 +1,5 @@ import { funGlobal_getAllBank } from "@/app_modules/_global/fun/get/fun_get_all_bank"; import { Investasi_UiMetodePembayaran } from "@/app_modules/investasi/_ui"; -import getMaster_NamaBank from "@/app_modules/investasi/fun/master/get_nama_bank"; export default async function Page({ params }: { params: { id: string } }) { const investasiId = params.id; diff --git a/src/app/lib/global_state.ts b/src/app/lib/global_state.ts index b26559c5..d8792fc0 100644 --- a/src/app/lib/global_state.ts +++ b/src/app/lib/global_state.ts @@ -13,12 +13,13 @@ export type ITypeStatusNotifikasi = | "Peserta Event" // VOTING | "Voting Masuk" - // DONASI + // DONASI & INVESTASI | "Berhasil" | "Proses" | "Menunggu" | "Gagal"; + /** * @param kategoriApp | "JOB", "VOTING", "EVENT", "DONASI", "INVESTASI", "COLLABORATION", "FORUM" * @type string diff --git a/src/app/zCoba/page.tsx b/src/app/zCoba/page.tsx index dd5cb2f4..148b1e76 100644 --- a/src/app/zCoba/page.tsx +++ b/src/app/zCoba/page.tsx @@ -1,8 +1,6 @@ "use client"; import { MainColor } from "@/app_modules/_global/color"; -import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; -import Coba_TestLoading from "@/app_modules/zCoba"; import { Avatar, Button, @@ -14,8 +12,6 @@ import { import { IconCamera } from "@tabler/icons-react"; import { useState } from "react"; import { DIRECTORY_ID } from "../lib"; -import { TokenStorage } from "../lib/token"; -import { envs } from "@/lib/envs"; export default function Page() { const [filePP, setFilePP] = useState(null); diff --git a/src/app_modules/admin/investasi/_component/button/button_konfirmasi_transaksi.tsx b/src/app_modules/admin/investasi/_component/button/button_konfirmasi_transaksi.tsx index 624a25ee..fcf8f02d 100644 --- a/src/app_modules/admin/investasi/_component/button/button_konfirmasi_transaksi.tsx +++ b/src/app_modules/admin/investasi/_component/button/button_konfirmasi_transaksi.tsx @@ -1,15 +1,22 @@ +"use client"; + +import { ComponentAdminGlobal_NotifikasiBerhasil } from "@/app_modules/admin/_admin_global/admin_notifikasi/notifikasi_berhasil"; +import { ComponentAdminGlobal_NotifikasiGagal } from "@/app_modules/admin/_admin_global/admin_notifikasi/notifikasi_gagal"; import { Button, Stack } from "@mantine/core"; +import { IconBan, IconCircleCheck } from "@tabler/icons-react"; +import { useState } from "react"; import { adminInvestasi_funAcceptTransaksiById, adminInvestasi_funGetAllTransaksiById, adminInvestasi_funRejectInvoiceById, } from "../../fun"; -import { ComponentAdminGlobal_NotifikasiBerhasil } from "@/app_modules/admin/_admin_global/admin_notifikasi/notifikasi_berhasil"; -import { ComponentAdminGlobal_NotifikasiGagal } from "@/app_modules/admin/_admin_global/admin_notifikasi/notifikasi_gagal"; -import { useState } from "react"; -import { IconCircleCheck } from "@tabler/icons-react"; -import { IconBan } from "@tabler/icons-react"; -import { MODEL_INVOICE_INVESTASI } from "@/app_modules/investasi/_lib/interface"; +import { clientLogger } from "@/util/clientLogger"; +import { IRealtimeData } from "@/app/lib/global_state"; +import { + notifikasiToAdmin_funCreate, + notifikasiToUser_funCreate, +} from "@/app_modules/notifikasi/fun"; +import { WibuRealtime } from "wibu-pkg"; export function AdminInvestasi_ComponentButtonKonfirmasiTransaksi({ invoiceId, @@ -26,47 +33,112 @@ export function AdminInvestasi_ComponentButtonKonfirmasiTransaksi({ const [isLoadingReject, setLoadingReject] = useState(false); async function onReject() { - const res = await adminInvestasi_funRejectInvoiceById({ invoiceId }); - if (res.status == 200) { - try { - const dataTransaksi = await adminInvestasi_funGetAllTransaksiById({ - investasiId, - page: 1, + try { + setLoadingReject(true); + + const res = await adminInvestasi_funRejectInvoiceById({ invoiceId }); + if (res.status == 200) { + const notifikasiInvestor: IRealtimeData = { + appId: invoiceId as string, + userId: res.userId as string, + status: res.statusName as any, + pesan: "Transaksi anda gagal, coba hubungi admin", + kategoriApp: "INVESTASI", + title: "Transaksi Gagal", + }; + + const notifToInvestor = await notifikasiToUser_funCreate({ + data: notifikasiInvestor as any, }); - onLoadData(dataTransaksi); - } catch (error) { - console.log(error); - } finally { - ComponentAdminGlobal_NotifikasiBerhasil(res.message); - setLoadingReject(true); + + if (notifToInvestor.status === 201) { + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "USER", + dataMessage: notifikasiInvestor, + }); + + const dataTransaksi = await adminInvestasi_funGetAllTransaksiById({ + investasiId, + page: 1, + }); + onLoadData(dataTransaksi); + + ComponentAdminGlobal_NotifikasiBerhasil(res.message); + } + } else { + ComponentAdminGlobal_NotifikasiGagal(res.message); } - } else { - ComponentAdminGlobal_NotifikasiGagal(res.message); + } catch (error) { + clientLogger.error("Error rejected investasi:", error); + } finally { + setLoadingReject(false); } } async function onAccept() { - const res = await adminInvestasi_funAcceptTransaksiById({ - invoiceId, - investasiId, - lembarTerbeli, - }); + try { + setLoadingAccept(true); + const res = await adminInvestasi_funAcceptTransaksiById({ + invoiceId, + investasiId, + lembarTerbeli, + }); + + if (res.status == 200) { + const dataNotifikasi: IRealtimeData = { + appId: investasiId, + status: res.data?.dataInvestasi?.MasterStatusInvestasi?.name as any, + userId: res.data?.dataInvestasi.authorId as string, + pesan: "Cek investasi anda, Anda memiliki investor baru", + kategoriApp: "INVESTASI", + title: "Investor baru", + }; + + const notif = await notifikasiToUser_funCreate({ + data: dataNotifikasi as any, + }); + + if (notif.status === 201) { + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "USER", + dataMessage: dataNotifikasi, + }); + } + + const notifikasiInvestor: IRealtimeData = { + appId: res.data?.dataInvestor.id as string, + status: "Berhasil", + userId: res.data?.dataInvestor.authorId as string, + pesan: "Selamat, anda telah menjadi investor baru", + kategoriApp: "INVESTASI", + title: "Investasi berhasil", + }; + + const notifToInvestor = await notifikasiToUser_funCreate({ + data: notifikasiInvestor as any, + }); + + if (notifToInvestor.status === 201) { + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "USER", + dataMessage: notifikasiInvestor, + }); + } - if (res.status == 200) { - try { const dataTransaksi = await adminInvestasi_funGetAllTransaksiById({ investasiId, page: 1, }); onLoadData(dataTransaksi); - } catch (error) { - console.log(error); - } finally { ComponentAdminGlobal_NotifikasiBerhasil(res.message); - setLoadingAccept(true); } - } else { - ComponentAdminGlobal_NotifikasiGagal(res.message); + } catch (error) { + clientLogger.error("Error accept invoice", error); + } finally { + setLoadingAccept(false); } } diff --git a/src/app_modules/admin/investasi/detail/detail_publish.tsx b/src/app_modules/admin/investasi/detail/detail_publish.tsx index 141d562c..087b61e1 100644 --- a/src/app_modules/admin/investasi/detail/detail_publish.tsx +++ b/src/app_modules/admin/investasi/detail/detail_publish.tsx @@ -74,7 +74,6 @@ export function AdminInvestasi_DetailPublish({ investasiId={investasiId} /> ) : null} - {/* {selectPage == "3" ? : null} */}
); diff --git a/src/app_modules/admin/investasi/fun/edit/fun_accept_invoice_by_id.ts b/src/app_modules/admin/investasi/fun/edit/fun_accept_invoice_by_id.ts index 61081ad6..751c1cdf 100644 --- a/src/app_modules/admin/investasi/fun/edit/fun_accept_invoice_by_id.ts +++ b/src/app_modules/admin/investasi/fun/edit/fun_accept_invoice_by_id.ts @@ -60,15 +60,26 @@ export async function adminInvestasi_funAcceptTransaksiById({ lembarTerbeli: resultLembarTerbeli.toString(), progress: resultProgres, }, + include: { + MasterStatusInvestasi: true, + }, }); if (!updateInvestasi) return { status: 400, message: "Gagal Update Data Investasi" }; + const newData = updateInvestasi; + + const allData = { + dataInvestasi: updateInvestasi, + dataInvestor: updt, + }; + revalidatePath(RouterAdminInvestasi.detail_publish); return { status: 200, message: "Update Berhasil", + data: allData, }; } } diff --git a/src/app_modules/admin/investasi/fun/edit/fun_reject_invoice_by_id.ts b/src/app_modules/admin/investasi/fun/edit/fun_reject_invoice_by_id.ts index 1facc575..fafa01ff 100644 --- a/src/app_modules/admin/investasi/fun/edit/fun_reject_invoice_by_id.ts +++ b/src/app_modules/admin/investasi/fun/edit/fun_reject_invoice_by_id.ts @@ -9,9 +9,6 @@ export async function adminInvestasi_funRejectInvoiceById({ }: { invoiceId: string; }) { - - - const updt = await prisma.investasi_Invoice.update({ where: { id: invoiceId, @@ -19,12 +16,19 @@ export async function adminInvestasi_funRejectInvoiceById({ data: { statusInvoiceId: "4", }, + select: { + StatusInvoice: true, + authorId: true, + }, }); - if (!updt) return { status: 400, message: "Gagal Update" }; + if (!updt) + return { status: 400, message: "Gagal Melakukan Reject", statusName: "" , userId: ""}; revalidatePath(RouterAdminInvestasi.detail_publish); return { status: 200, - message: "Update Berhasil", + message: "Reject Berhasil", + statusName: updt.StatusInvoice?.name, + userId: updt.authorId, }; } diff --git a/src/app_modules/admin/investasi/main/table_review.tsx b/src/app_modules/admin/investasi/main/table_review.tsx index 432241e4..4b384a80 100644 --- a/src/app_modules/admin/investasi/main/table_review.tsx +++ b/src/app_modules/admin/investasi/main/table_review.tsx @@ -62,12 +62,7 @@ function TableView({ listData }: { listData: any }) { useShallowEffect(() => { if (isAdminInvestasi_TriggerReview) { setIsShowReload(false); - } - }, [isAdminInvestasi_TriggerReview]); - - useShallowEffect(() => { - if (isAdminInvestasi_TriggerReview) { - setIsShowReload(true); + setIsAdminInvestasi_TriggerReview(false) } }, [isAdminInvestasi_TriggerReview]); diff --git a/src/app_modules/admin/notifikasi/route_setting/donasi.ts b/src/app_modules/admin/notifikasi/route_setting/donasi.ts index 467184e4..10c2aec6 100644 --- a/src/app_modules/admin/notifikasi/route_setting/donasi.ts +++ b/src/app_modules/admin/notifikasi/route_setting/donasi.ts @@ -1,16 +1,12 @@ -import { RouterAdminDonasi } from "@/app/lib/router_admin/router_admin_donasi"; -import { RouterAdminDonasi_OLD } from "@/app/lib/router_hipmi/router_admin"; -import { MODEL_NOTIFIKASI } from "@/app_modules/notifikasi/model/interface"; -import { data } from "autoprefixer"; -import _ from "lodash"; -import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; -import { IAdmin_ActivePage, IAdmin_ActiveChildId } from "./type_of_select_page"; -import { admin_funDonasiCheckStatus } from "../fun/get/fun_donasi_check_status"; -import adminNotifikasi_funUpdateIsReadById from "../fun/update/fun_update_is_read_by_id"; -import adminNotifikasi_countNotifikasi from "../fun/count/count_is_read"; -import adminNotifikasi_getByUserId from "../fun/get/get_notifikasi_by_user_id"; -import { ComponentAdminGlobal_NotifikasiPeringatan } from "../../_admin_global/admin_notifikasi/notifikasi_peringatan"; import { ITypeStatusNotifikasi } from "@/app/lib/global_state"; +import { RouterAdminDonasi_OLD } from "@/app/lib/router_hipmi/router_admin"; +import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; +import { ComponentAdminGlobal_NotifikasiPeringatan } from "../../_admin_global/admin_notifikasi/notifikasi_peringatan"; +import adminNotifikasi_countNotifikasi from "../fun/count/count_is_read"; +import { admin_funDonasiCheckStatus } from "../fun/get/fun_donasi_check_status"; +import adminNotifikasi_getByUserId from "../fun/get/get_notifikasi_by_user_id"; +import adminNotifikasi_funUpdateIsReadById from "../fun/update/fun_update_is_read_by_id"; +import { IAdmin_ActiveChildId, IAdmin_ActivePage } from "./type_of_select_page"; export default async function adminNotifikasi_findRouterDonasi({ appId, @@ -38,13 +34,29 @@ export default async function adminNotifikasi_findRouterDonasi({ status == "Proses" || status == "Gagal" ) { - const path = RouterAdminDonasi_OLD.detail_publish + appId; - router.push(path, { scroll: false }); - onChangeNavbar({ - id: "Donasi", - childId: "Donasi_2", + const udpateReadNotifikasi = await adminNotifikasi_funUpdateIsReadById({ + notifId: notifikasiId, }); + if (udpateReadNotifikasi.status == 200) { + const loadCountNotif = await adminNotifikasi_countNotifikasi(); + onLoadCountNotif(loadCountNotif); + + const loadListNotifikasi = await adminNotifikasi_getByUserId({ + page: 1, + }); + onLoadDataNotifikasi(loadListNotifikasi); + const path = RouterAdminDonasi_OLD.detail_publish + appId; + router.push(path, { scroll: false }); + onChangeNavbar({ + id: "Donasi", + childId: "Donasi_2", + }); + } else { + ComponentAdminGlobal_NotifikasiPeringatan("Status tidak ditemukan"); + return false; + } + return true; } else { const check = await admin_funDonasiCheckStatus({ id: appId }); diff --git a/src/app_modules/admin/notifikasi/route_setting/investasi.ts b/src/app_modules/admin/notifikasi/route_setting/investasi.ts index 5a92413f..a235f875 100644 --- a/src/app_modules/admin/notifikasi/route_setting/investasi.ts +++ b/src/app_modules/admin/notifikasi/route_setting/investasi.ts @@ -6,6 +6,7 @@ import { admin_funInvestasiCheckStatus } from "../fun/get/fun_investasi_check_st import adminNotifikasi_getByUserId from "../fun/get/get_notifikasi_by_user_id"; import adminNotifikasi_funUpdateIsReadById from "../fun/update/fun_update_is_read_by_id"; import { IAdmin_ActiveChildId, IAdmin_ActivePage } from "./type_of_select_page"; +import { RouterAdminInvestasi } from "@/app/lib/router_admin/router_admin_investasi"; export default async function adminNotifikasi_findRouterInvestasi({ appId, @@ -27,13 +28,15 @@ export default async function adminNotifikasi_findRouterInvestasi({ childId: IAdmin_ActiveChildId; }) => void; }) { - const check = await admin_funInvestasiCheckStatus({ id: appId }); - - if (check.status == 200) { + if ( + status == "Menunggu" || + status == "Berhasil" || + status == "Proses" || + status == "Gagal" + ) { const udpateReadNotifikasi = await adminNotifikasi_funUpdateIsReadById({ notifId: notifikasiId, }); - if (udpateReadNotifikasi.status == 200) { const loadCountNotif = await adminNotifikasi_countNotifikasi(); onLoadCountNotif(loadCountNotif); @@ -42,59 +45,72 @@ export default async function adminNotifikasi_findRouterInvestasi({ page: 1, }); onLoadDataNotifikasi(loadListNotifikasi); + const path = RouterAdminInvestasi.detail_publish + appId; + router.push(path, { scroll: false }); - const path = `/dev/admin/investasi/sub-menu/${check.statusName}`; - - if (check.statusName == "draft") { - ComponentAdminGlobal_NotifikasiPeringatan( - "Status telah dirubah oleh user" - ); - } else { - if (check.statusName == "publish") { - onChangeNavbar({ - id: "Investasi", - childId: "Investasi_2", - }); - } - - if (check.statusName == "review") { - onChangeNavbar({ - id: "Investasi", - childId: "Investasi_3", - }); - } - - if (check.statusName == "reject") { - onChangeNavbar({ - id: "Investasi", - childId: "Investasi_4", - }); - } - - router.push(path, { scroll: false }); - } + onChangeNavbar({ + id: "Investasi", + childId: "Investasi_2", + }); return true; + } else { + ComponentAdminGlobal_NotifikasiPeringatan("Status tidak ditemukan"); + return false; } } else { - ComponentAdminGlobal_NotifikasiPeringatan("Status tidak ditemukan"); - return false; + const check = await admin_funInvestasiCheckStatus({ id: appId }); + + if (check.status == 200) { + const udpateReadNotifikasi = await adminNotifikasi_funUpdateIsReadById({ + notifId: notifikasiId, + }); + + if (udpateReadNotifikasi.status == 200) { + const loadCountNotif = await adminNotifikasi_countNotifikasi(); + onLoadCountNotif(loadCountNotif); + + const loadListNotifikasi = await adminNotifikasi_getByUserId({ + page: 1, + }); + onLoadDataNotifikasi(loadListNotifikasi); + + const path = `/dev/admin/investasi/sub-menu/${check.statusName}`; + + if (check.statusName == "draft") { + ComponentAdminGlobal_NotifikasiPeringatan( + "Status telah dirubah oleh user" + ); + } else { + if (check.statusName == "publish") { + onChangeNavbar({ + id: "Investasi", + childId: "Investasi_2", + }); + } + + if (check.statusName == "review") { + onChangeNavbar({ + id: "Investasi", + childId: "Investasi_3", + }); + } + + if (check.statusName == "reject") { + onChangeNavbar({ + id: "Investasi", + childId: "Investasi_4", + }); + } + + router.push(path, { scroll: false }); + } + + return true; + } + } else { + ComponentAdminGlobal_NotifikasiPeringatan("Status tidak ditemukan"); + return false; + } } - // if ( - // status == "Menunggu" || - // status == "Berhasil" || - // status == "Proses" || - // status == "Gagal" - // ) { - // const path = RouterAdminDonasi_OLD.detail_publish + appId; - // router.push(path, { scroll: false }); - // onChangeNavbar({ - // id: "Donasi", - // childId: "Donasi_2", - // }); - - // return true; - // } else { - - // } } diff --git a/src/app_modules/admin/notifikasi/view_card_drawer.tsx b/src/app_modules/admin/notifikasi/view_card_drawer.tsx index 57484dc9..b57bd44a 100644 --- a/src/app_modules/admin/notifikasi/view_card_drawer.tsx +++ b/src/app_modules/admin/notifikasi/view_card_drawer.tsx @@ -13,18 +13,16 @@ import { IconCheck, IconChecks } from "@tabler/icons-react"; import { useAtom } from "jotai"; import { useRouter } from "next/navigation"; import { useState } from "react"; -import { ComponentAdminGlobal_NotifikasiPeringatan } from "../_admin_global/admin_notifikasi/notifikasi_peringatan"; -import adminNotifikasi_countNotifikasi from "./fun/count/count_is_read"; -import adminNotifikasi_getByUserId from "./fun/get/get_notifikasi_by_user_id"; +import adminNotifikasi_findRouterDonasi from "./route_setting/donasi"; import { adminNotifikasi_findRouterEvent } from "./route_setting/event"; +import adminNotifikasi_findRouterInvestasi from "./route_setting/investasi"; import { adminNotifikasi_findRouterJob } from "./route_setting/job"; import { IAdmin_ActiveChildId, IAdmin_ActivePage, } from "./route_setting/type_of_select_page"; import { adminNotifikasi_findRouterVoting } from "./route_setting/voting"; -import adminNotifikasi_findRouterDonasi from "./route_setting/donasi"; -import adminNotifikasi_findRouterInvestasi from "./route_setting/investasi"; +import { clientLogger } from "@/util/clientLogger"; export default function AdminNotifikasi_ViewCardDrawer({ data, @@ -64,199 +62,200 @@ export default function AdminNotifikasi_ViewCardDrawer({ async function onRead() { // ========================== JOB ========================== // - if (data?.kategoriApp === "JOB") { + try { setVisible(true); - setDataId(data.id); - const checkJob = await adminNotifikasi_findRouterJob({ - appId: data.appId, - notifikasiId: data.id, - router: router, - activePage: activePage, - onLoadCountNotif(val) { - onLoadCountNotif(val); - }, - onLoadDataNotifikasi(val) { - onLoadDataNotifikasi(val); - }, - onChangeNavbar(val) { - onChangeNavbar({ - id: val.id, - childId: val.childId, - }); - }, - }); + if (data?.kategoriApp === "JOB") { + setDataId(data.id); - if (checkJob) { - setIsAdminJob_TriggerReview(false); - setVisible(false); - setDataId(""); - onToggleNavbar(false); + const checkJob = await adminNotifikasi_findRouterJob({ + appId: data.appId, + notifikasiId: data.id, + router: router, + activePage: activePage, + onLoadCountNotif(val) { + onLoadCountNotif(val); + }, + onLoadDataNotifikasi(val) { + onLoadDataNotifikasi(val); + }, + onChangeNavbar(val) { + onChangeNavbar({ + id: val.id, + childId: val.childId, + }); + }, + }); + + if (checkJob) { + setIsAdminJob_TriggerReview(false); + setVisible(false); + setDataId(""); + onToggleNavbar(false); + } + + return; } - } - // ========================== JOB ========================== // + // ========================== JOB ========================== // - // ========================== EVENT ========================== // + // ========================== EVENT ========================== // - if (data.kategoriApp == "EVENT") { - setVisible(true); - setDataId(data.id); + if (data.kategoriApp == "EVENT") { + setDataId(data.id); - const checkEvent = await adminNotifikasi_findRouterEvent({ - appId: data.appId, - notifikasiId: data.id, - router: router, - activePage: activePage, - onLoadCountNotif(val) { - onLoadCountNotif(val); - }, - onLoadDataNotifikasi(val) { - onLoadDataNotifikasi(val); - }, - onChangeNavbar(val) { - onChangeNavbar({ - id: val.id, - childId: val.childId, - }); - }, - }); + const checkEvent = await adminNotifikasi_findRouterEvent({ + appId: data.appId, + notifikasiId: data.id, + router: router, + activePage: activePage, + onLoadCountNotif(val) { + onLoadCountNotif(val); + }, + onLoadDataNotifikasi(val) { + onLoadDataNotifikasi(val); + }, + onChangeNavbar(val) { + onChangeNavbar({ + id: val.id, + childId: val.childId, + }); + }, + }); - if (checkEvent) { - setIsAdminEvent_TriggerReview(false); - setVisible(false); - setDataId(""); - onToggleNavbar(false); + if (checkEvent) { + setIsAdminEvent_TriggerReview(false); + setVisible(false); + setDataId(""); + onToggleNavbar(false); + } + + return; } - } - // ========================== EVENT ========================== // + // ========================== EVENT ========================== // - // ========================== VOTING ========================== // + // ========================== VOTING ========================== // - if (data.kategoriApp == "VOTING") { - setVisible(true); - setDataId(data.id); + if (data.kategoriApp == "VOTING") { + setDataId(data.id); - const checkVoting = await adminNotifikasi_findRouterVoting({ - router: router, - appId: data.appId, - notifikasiId: data.id, - activePage: activePage, - onLoadCountNotif(val) { - onLoadCountNotif(val); - }, - onLoadDataNotifikasi(val) { - onLoadDataNotifikasi(val); - }, - onChangeNavbar(val) { - onChangeNavbar({ - id: val.id, - childId: val.childId, - }); - }, - }); + const checkVoting = await adminNotifikasi_findRouterVoting({ + router: router, + appId: data.appId, + notifikasiId: data.id, + activePage: activePage, + onLoadCountNotif(val) { + onLoadCountNotif(val); + }, + onLoadDataNotifikasi(val) { + onLoadDataNotifikasi(val); + }, + onChangeNavbar(val) { + onChangeNavbar({ + id: val.id, + childId: val.childId, + }); + }, + }); - if (checkVoting) { - setIsAdminVoting_TriggerReview(false); - setVisible(false); - setDataId(""); - onToggleNavbar(false); + if (checkVoting) { + setIsAdminVoting_TriggerReview(false); + setVisible(false); + setDataId(""); + onToggleNavbar(false); + } + + return; } - } - // ========================== VOTING ========================== // + // ========================== VOTING ========================== // - // ========================== DONASI ========================== // + // ========================== DONASI ========================== // - if (data.kategoriApp == "DONASI") { - setVisible(true); - setDataId(data.id); + if (data.kategoriApp == "DONASI") { + setDataId(data.id); - const checkDonasi = await adminNotifikasi_findRouterDonasi({ - appId: data.appId, - notifikasiId: data.id, - router: router, - status: data.status as ITypeStatusNotifikasi, - onLoadCountNotif(val) { - onLoadCountNotif(val); - }, - onLoadDataNotifikasi(val) { - onLoadDataNotifikasi(val); - }, - onChangeNavbar(val) { - onChangeNavbar({ - id: val.id, - childId: val.childId, - }); - }, - }); + const checkDonasi = await adminNotifikasi_findRouterDonasi({ + appId: data.appId, + notifikasiId: data.id, + router: router, + status: data.status as ITypeStatusNotifikasi, + onLoadCountNotif(val) { + onLoadCountNotif(val); + }, + onLoadDataNotifikasi(val) { + onLoadDataNotifikasi(val); + }, + onChangeNavbar(val) { + onChangeNavbar({ + id: val.id, + childId: val.childId, + }); + }, + }); - if (checkDonasi) { - setIsAdminDonasi_TriggerReview(false); - setVisible(false); - setDataId(""); - onToggleNavbar(false); + if (checkDonasi) { + setIsAdminDonasi_TriggerReview(false); + setVisible(false); + setDataId(""); + onToggleNavbar(false); + } + + return; } - } - // ========================== DONASI ========================== // + // ========================== DONASI ========================== // - // ========================== INVESTASI ========================== // + // ========================== INVESTASI ========================== // - if (data.kategoriApp == "INVESTASI") { - setVisible(true); - setDataId(data.id); + if (data.kategoriApp == "INVESTASI") { + setDataId(data.id); - const checkInvestasi = await adminNotifikasi_findRouterInvestasi({ - appId: data.appId, - notifikasiId: data.id, - status: data.status as ITypeStatusNotifikasi, - router: router, - onLoadCountNotif(val) { - onLoadCountNotif(val); - }, - onLoadDataNotifikasi(val) { - onLoadDataNotifikasi(val); - }, - onChangeNavbar(val) { - onChangeNavbar({ - id: val.id, - childId: val.childId, - }); - }, - }); + const checkInvestasi = await adminNotifikasi_findRouterInvestasi({ + appId: data.appId, + notifikasiId: data.id, + status: data.status as ITypeStatusNotifikasi, + router: router, + onLoadCountNotif(val) { + onLoadCountNotif(val); + }, + onLoadDataNotifikasi(val) { + onLoadDataNotifikasi(val); + }, + onChangeNavbar(val) { + onChangeNavbar({ + id: val.id, + childId: val.childId, + }); + }, + }); - if (checkInvestasi) { - setIsAdminDonasi_TriggerReview(false); - setVisible(false); - setDataId(""); - onToggleNavbar(false); + if (checkInvestasi) { + setIsAdminDonasi_TriggerReview(false); + setVisible(false); + setDataId(""); + onToggleNavbar(false); + } + + return; } + + // // FORUM + // e?.kategoriApp === "FORUM" && + // adminNotifikasi_findRouterForum({ + // data: e, + // router: router, + // onChangeNavbar(val) { + // onChangeNavbar(val); + // }, + // onToggleNavbar(val) { + // onToggleNavbar(val); + // }, + // }); + + } catch (error) { + clientLogger.error("Error notifikasi function", error); + } finally { + setVisible(false); } - - // // FORUM - // e?.kategoriApp === "FORUM" && - // adminNotifikasi_findRouterForum({ - // data: e, - // router: router, - // onChangeNavbar(val) { - // onChangeNavbar(val); - // }, - // onToggleNavbar(val) { - // onToggleNavbar(val); - // }, - // }); - - // // VOTE - // e?.kategoriApp === "VOTING" && - // adminNotifikasi_findRouterVoting({ - // data: e, - // router: router, - // onChangeNavbar(val) { - // onChangeNavbar(val); - // }, - // onToggleNavbar(val) { - // onToggleNavbar(val); - // }, - // }); } return ( diff --git a/src/app_modules/investasi/_view/status_transaksi/view_invoice_berhasil.tsx b/src/app_modules/investasi/_view/status_transaksi/view_invoice_berhasil.tsx index 9b20f7d5..87317b8a 100644 --- a/src/app_modules/investasi/_view/status_transaksi/view_invoice_berhasil.tsx +++ b/src/app_modules/investasi/_view/status_transaksi/view_invoice_berhasil.tsx @@ -1,19 +1,20 @@ "use client"; -import { RouterAdminInvestasi } from "@/app/lib/router_admin/router_admin_investasi"; import { AccentColor } from "@/app_modules/_global/color/color_pallet"; -import { ComponentGlobal_LoadImage, ComponentGlobal_TampilanRupiah } from "@/app_modules/_global/component"; +import { + ComponentGlobal_LoadImage, + ComponentGlobal_TampilanRupiah, +} from "@/app_modules/_global/component"; import { Box, Button, Collapse, Grid, Group, - Image, Paper, Stack, Text, - Title + Title, } from "@mantine/core"; import { IconBrandCashapp } from "@tabler/icons-react"; import { useRouter } from "next/navigation"; @@ -157,12 +158,7 @@ export function Investasi_ViewTransaksiBerhasil({ transitionDuration={500} transitionTimingFunction="linear" > - - - {/* */} +
diff --git a/src/app_modules/investasi/_view/status_transaksi/view_transaksi_gagal.tsx b/src/app_modules/investasi/_view/status_transaksi/view_transaksi_gagal.tsx index e0fcfb16..47a04eb8 100644 --- a/src/app_modules/investasi/_view/status_transaksi/view_transaksi_gagal.tsx +++ b/src/app_modules/investasi/_view/status_transaksi/view_transaksi_gagal.tsx @@ -2,7 +2,7 @@ import { RouterAdminInvestasi } from "@/app/lib/router_admin/router_admin_investasi"; import { AccentColor } from "@/app_modules/_global/color/color_pallet"; -import { ComponentGlobal_TampilanRupiah } from "@/app_modules/_global/component"; +import { ComponentGlobal_LoadImage, ComponentGlobal_TampilanRupiah } from "@/app_modules/_global/component"; import { ActionIcon, Box, @@ -170,10 +170,7 @@ export function Investasi_ViewTransaksiGagal({ transitionDuration={500} transitionTimingFunction="linear" > - +
diff --git a/src/app_modules/investasi/_view/transaksi/view_invoice.tsx b/src/app_modules/investasi/_view/transaksi/view_invoice.tsx index 775f869c..ac6855c7 100644 --- a/src/app_modules/investasi/_view/transaksi/view_invoice.tsx +++ b/src/app_modules/investasi/_view/transaksi/view_invoice.tsx @@ -28,6 +28,10 @@ import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_glo import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun"; import { DIRECTORY_ID } from "@/app/lib"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; +import { IRealtimeData } from "@/app/lib/global_state"; +import { notifikasiToAdmin_funCreate } from "@/app_modules/notifikasi/fun"; +import { WibuRealtime } from "wibu-pkg"; +import { clientLogger } from "@/util/clientLogger"; export function Investasi_ViewInvoice({ dataInvoice, @@ -40,25 +44,59 @@ export function Investasi_ViewInvoice({ const [file, setFile] = useState(null); async function onUpload() { - const uploadFileToStorage = await funGlobal_UploadToStorage({ - file: file as any, - dirId: DIRECTORY_ID.investasi_bukti_transfer, - }); + try { + setLoading(true); + const uploadFileToStorage = await funGlobal_UploadToStorage({ + file: file as any, + dirId: DIRECTORY_ID.investasi_bukti_transfer, + }); - if (!uploadFileToStorage.success) - return ComponentGlobal_NotifikasiPeringatan("Gagal upload bukti transfer") + if (!uploadFileToStorage.success) { + ComponentGlobal_NotifikasiPeringatan("Gagal upload bukti transfer"); + return; + } - const res = await investasi_funUploadBuktiTransferById({ - invoiceId: data.id, - fileId: uploadFileToStorage.data.id, - }); + const res = await investasi_funUploadBuktiTransferById({ + invoiceId: data.id, + fileId: uploadFileToStorage.data.id, + }); - if (res.status !== 200) return ComponentGlobal_NotifikasiGagal(res.message); - ComponentGlobal_NotifikasiBerhasil(res.message); - setLoading(true); - router.push(NEW_RouterInvestasi.proses_transaksi + data.id, { - scroll: false, - }); + if (res.status != 200) { + ComponentGlobal_NotifikasiGagal(res.message); + return; + } + + const dataNotifikasi: IRealtimeData = { + appId: dataInvoice.Investasi.id, + status: "Proses", + userId: dataInvoice.authorId as string, + pesan: "Bukti transfer telah diupload", + kategoriApp: "INVESTASI", + title: "Invoice baru", + }; + + const notif = await notifikasiToAdmin_funCreate({ + data: dataNotifikasi as any, + }); + + if (notif.status === 201) { + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "ADMIN", + dataMessage: dataNotifikasi, + }); + + ComponentGlobal_NotifikasiBerhasil(res.message); + + router.push(NEW_RouterInvestasi.proses_transaksi + data.id, { + scroll: false, + }); + } + } catch (error) { + clientLogger.error(" Error upload invoice", error); + } finally { + setLoading(false); + } } return ( diff --git a/src/app_modules/investasi/_view/transaksi/view_metode_pembayaran.tsx b/src/app_modules/investasi/_view/transaksi/view_metode_pembayaran.tsx index c0ed8d22..eabb3f3d 100644 --- a/src/app_modules/investasi/_view/transaksi/view_metode_pembayaran.tsx +++ b/src/app_modules/investasi/_view/transaksi/view_metode_pembayaran.tsx @@ -1,17 +1,20 @@ +import { IRealtimeData } from "@/app/lib/global_state"; import { NEW_RouterInvestasi } from "@/app/lib/router_hipmi/router_investasi"; import { AccentColor, MainColor, } from "@/app_modules/_global/color/color_pallet"; -import { Button, Paper, Radio, Stack, Text, Title } from "@mantine/core"; +import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; +import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan"; +import { notifikasiToAdmin_funCreate } from "@/app_modules/notifikasi/fun"; +import { clientLogger } from "@/util/clientLogger"; +import { Button, Paper, Radio, Stack, Title } from "@mantine/core"; import { useLocalStorage } from "@mantine/hooks"; import { useRouter } from "next/navigation"; import { useState } from "react"; -import { MODEL_MASTER_BANK } from "../../_lib/interface"; +import { WibuRealtime } from "wibu-pkg"; import { investasi_funCreateInvoice } from "../../_fun/create/fun_create_invoice"; -import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan"; -import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; -import { data } from "autoprefixer"; +import { MODEL_MASTER_BANK } from "../../_lib/interface"; export function Investasi_ViewMetodePembayaran({ listBank, @@ -34,20 +37,50 @@ export function Investasi_ViewMetodePembayaran({ }); async function onProses() { - const res = await investasi_funCreateInvoice({ - data: { - total: total, - pilihBank: pilihBank, - investasiId: investasiId, - jumlah: jumlah, - }, - }); + try { + const res = await investasi_funCreateInvoice({ + data: { + total: total, + pilihBank: pilihBank, + investasiId: investasiId, + jumlah: jumlah, + }, + }); - if (res.status !== 201) - return ComponentGlobal_NotifikasiPeringatan(res.message); - ComponentGlobal_NotifikasiBerhasil(res.message); - setLoading(true); - router.push(NEW_RouterInvestasi.invoice + res.data?.id, { scroll: false }); + if (res.status != 201) { + ComponentGlobal_NotifikasiPeringatan(res.message); + return; + } + + const dataNotifikasi: IRealtimeData = { + appId: investasiId, + status: "Menunggu", + userId: res.data?.authorId as string, + pesan: "Menunggu transfer", + kategoriApp: "INVESTASI", + title: "Transaksi baru", + }; + + const notif = await notifikasiToAdmin_funCreate({ + data: dataNotifikasi as any, + }); + + if (notif.status === 201) { + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "ADMIN", + dataMessage: dataNotifikasi, + }); + + ComponentGlobal_NotifikasiBerhasil(res.message); + setLoading(true); + router.push(NEW_RouterInvestasi.invoice + res.data?.id, { + scroll: false, + }); + } + } catch (error) { + clientLogger.error("Error create invoice:", error); + } } return ( diff --git a/src/app_modules/notifikasi/component/path/investasi.ts b/src/app_modules/notifikasi/component/path/investasi.ts index 6a51e236..0cf3fb65 100644 --- a/src/app_modules/notifikasi/component/path/investasi.ts +++ b/src/app_modules/notifikasi/component/path/investasi.ts @@ -8,6 +8,10 @@ import notifikasi_countUserNotifikasi from "../../fun/count/fun_count_by_id"; import notifikasi_funUpdateIsReadById from "../../fun/update/fun_update_is_read_by_user_id"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global"; import { notifikasi_funInvestasiCheckStatus } from "../../fun/check/fun_check_investasi_status"; +import { + notifikasi_funGetStatusTransaksiById, + notifikasi_funInvestasiChecInvestaorStatus, +} from "../../fun"; export async function redirectInvestasiPage({ appId, @@ -29,25 +33,22 @@ export async function redirectInvestasiPage({ onLoadCountNtf(val: number): void; }) { const check = await notifikasi_funInvestasiCheckStatus({ id: appId }); + const checkInvestor = await notifikasi_funInvestasiChecInvestaorStatus({ + id: appId, + }); + + console.log("check", check); + console.log("check investor:", checkInvestor); if (check.status == 200) { - // const loadListNotifikasi = await notifikasi_getByUserId({ - // page: 1, - // kategoriApp: categoryPage as any, - // }); - // onLoadDataEvent(loadListNotifikasi); - - // const loadCountNotifikasi = await notifikasi_countUserNotifikasi(); - // onLoadCountNtf(loadCountNotifikasi); - const updateReadNotifikasi = await notifikasi_funUpdateIsReadById({ notifId: dataId, }); if (updateReadNotifikasi.status == 200) { onSetVisible(true); - onSetMenuId(1); + if (check.statusName == "publish") { router.push(`/dev/investasi/detail/${appId}`, { scroll: false }); } else { @@ -55,6 +56,25 @@ export async function redirectInvestasiPage({ router.push(path, { scroll: false }); } } + } else if (checkInvestor.status == 200) { + const updateReadNotifikasi = await notifikasi_funUpdateIsReadById({ + notifId: dataId, + }); + + if (updateReadNotifikasi.status == 200) { + onSetVisible(true); + onSetMenuId(1); + + if (checkInvestor.statusName == "berhasil") { + const path = `/dev/investasi/detail/saham/${appId}`; + router.push(path, { scroll: false }); + } + + if (checkInvestor.statusName == "gagal") { + const path = `/dev/investasi/status-transaksi/gagal/${appId}`; + router.push(path, { scroll: false }); + } + } } else { ComponentGlobal_NotifikasiPeringatan("Status tidak ditemukan"); } diff --git a/src/app_modules/notifikasi/fun/check/fun_check_investasi_status.ts b/src/app_modules/notifikasi/fun/check/fun_check_investasi_status.ts index a9e87485..ce617c54 100644 --- a/src/app_modules/notifikasi/fun/check/fun_check_investasi_status.ts +++ b/src/app_modules/notifikasi/fun/check/fun_check_investasi_status.ts @@ -23,6 +23,7 @@ export async function notifikasi_funInvestasiCheckStatus({ message: "Investasi tidak ditemukan", statusName: "", }; + return { status: 200, message: "Berhasil di cek", diff --git a/src/app_modules/notifikasi/fun/check/fun_check_investor_status.ts b/src/app_modules/notifikasi/fun/check/fun_check_investor_status.ts new file mode 100644 index 00000000..80a0c903 --- /dev/null +++ b/src/app_modules/notifikasi/fun/check/fun_check_investor_status.ts @@ -0,0 +1,32 @@ +"use server"; + +import { prisma } from "@/app/lib"; +import _ from "lodash"; + +export async function notifikasi_funInvestasiChecInvestaorStatus({ + id, +}: { + id: string; +}) { + const data = await prisma.investasi_Invoice.findUnique({ + where: { + id: id, + }, + select: { + StatusInvoice: true, + }, + }); + + if (!data) + return { + status: 400, + message: "Investasi tidak ditemukan", + statusName: "", + }; + + return { + status: 200, + message: "Berhasil di cek", + statusName: _.lowerCase(data.StatusInvoice?.name), + }; +} diff --git a/src/app_modules/notifikasi/fun/create/create_notif_to_user.ts b/src/app_modules/notifikasi/fun/create/create_notif_to_user.ts index e6f5ce65..dc232709 100644 --- a/src/app_modules/notifikasi/fun/create/create_notif_to_user.ts +++ b/src/app_modules/notifikasi/fun/create/create_notif_to_user.ts @@ -2,23 +2,29 @@ import prisma from "@/app/lib/prisma"; import { MODEL_NOTIFIKASI } from "../../model/interface"; +import backendLogger from "@/util/backendLogger"; export default async function notifikasiToUser_funCreate({ data, }: { data: MODEL_NOTIFIKASI; }) { - const created = await prisma.notifikasi.create({ - data: { - userId: data.userId, - appId: data.appId, - status: data.status, - title: data.title, - pesan: data.pesan, - kategoriApp: data.kategoriApp, - userRoleId: "1", - }, - }); - if (!created) return { status: 400, message: "Gagal mengirim notifikasi" }; - return { status: 201, message: "Berhasil mengirim notifikasi" }; + try { + const created = await prisma.notifikasi.create({ + data: { + userId: data.userId, + appId: data.appId, + status: data.status, + title: data.title, + pesan: data.pesan, + kategoriApp: data.kategoriApp, + userRoleId: "1", + }, + }); + if (!created) return { status: 400, message: "Gagal mengirim notifikasi" }; + return { status: 201, message: "Berhasil mengirim notifikasi" }; + } catch (error) { + backendLogger.error("Gagal mengirim notifikasi", error); + return { status: 401, message: "Error server" }; + } } diff --git a/src/app_modules/notifikasi/fun/get/fun_get_status_transaksi.ts b/src/app_modules/notifikasi/fun/get/fun_get_status_transaksi.ts new file mode 100644 index 00000000..ef295853 --- /dev/null +++ b/src/app_modules/notifikasi/fun/get/fun_get_status_transaksi.ts @@ -0,0 +1,22 @@ +"use server"; + +import prisma from "@/app/lib/prisma"; + +export default async function notifikasi_funGetStatusTransaksiById({ + notifId, +}: { + notifId: string; +}) { + const data = await prisma.notifikasi.findFirst({ + where: { + id: notifId, + }, + select: { + status: true, + }, + }); + + + if (!data) return { status: 400 }; + return { status: 200, data: data }; +} diff --git a/src/app_modules/notifikasi/fun/index.ts b/src/app_modules/notifikasi/fun/index.ts index 8c097218..ca01304b 100644 --- a/src/app_modules/notifikasi/fun/index.ts +++ b/src/app_modules/notifikasi/fun/index.ts @@ -1,11 +1,15 @@ import { notifikasi_funEventCheckStatus } from "./check/fun_check_event_status"; +import { notifikasi_funInvestasiChecInvestaorStatus } from "./check/fun_check_investor_status"; import notifikasiToAdmin_funCreate from "./create/create_notif_to_admin"; import notifikasiToUser_funCreate from "./create/create_notif_to_user"; import { notifikasiToUser_CreateGroupCollaboration } from "./create/create_notif_to_user_collaboration"; import { notifikasiToUser_CreateKabarDonasi } from "./create/create_notif_to_user_kabar_donasi"; +import notifikasi_funGetStatusTransaksiById from "./get/fun_get_status_transaksi"; export { notifikasiToAdmin_funCreate }; export { notifikasiToUser_CreateGroupCollaboration }; export { notifikasiToUser_CreateKabarDonasi }; export { notifikasiToUser_funCreate }; export { notifikasi_funEventCheckStatus }; +export { notifikasi_funGetStatusTransaksiById }; +export { notifikasi_funInvestasiChecInvestaorStatus }; From cfc0ff5ff707896ff9680247a55cfc085dde847c Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Tue, 24 Dec 2024 16:48:18 +0800 Subject: [PATCH 089/595] chore(release): 1.2.34 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7d48516..1a5a8efa 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.34](https://github.com/bipproduction/hipmi/compare/v1.2.33...v1.2.34) (2024-12-24) + ## [1.2.33](https://github.com/bipproduction/hipmi/compare/v1.2.32...v1.2.33) (2024-12-22) ## [1.2.32](https://github.com/bipproduction/hipmi/compare/v1.2.31...v1.2.32) (2024-12-19) diff --git a/package.json b/package.json index 35d83a3c..04d7ebc3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.33", + "version": "1.2.34", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" From 5f8a1c38d0f53eddc53c016433a2628cc7b55778 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Thu, 26 Dec 2024 10:47:08 +0800 Subject: [PATCH 090/595] Fix assets image Deksripsi: - Asset image pada halaman home dan crowd --- src/app_modules/crowd/main/view.tsx | 35 ++++++++++---------- src/app_modules/home/component/body_home.tsx | 26 +++++++++++++-- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/app_modules/crowd/main/view.tsx b/src/app_modules/crowd/main/view.tsx index b3d4787d..f9ea5e9b 100644 --- a/src/app_modules/crowd/main/view.tsx +++ b/src/app_modules/crowd/main/view.tsx @@ -5,8 +5,6 @@ import { AccentColor, MainColor, } from "@/app_modules/_global/color/color_pallet"; -import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; -import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan"; import { gs_donasi_hot_menu } from "@/app_modules/donasi/global_state"; import { gs_investas_menu } from "@/app_modules/investasi/g_state"; import { Grid, Image, Paper, Stack, Text, Title } from "@mantine/core"; @@ -21,26 +19,27 @@ export default function MainCrowd() { const [donasiHotMenu, setDonasiHotMenu] = useAtom(gs_donasi_hot_menu); const [loadingInv, setLoadingInv] = useState(false); const [loadingDon, setLoadingDon] = useState(false); + const [isLoading, setLoading] = useState(true); return ( <> - - {/* - - - */} - - + setLoading(false)} + styles={{ + imageWrapper: { + border: `2px solid ${AccentColor.blue}`, + borderRadius: "10px 10px 10px 10px", + }, + image: { + borderRadius: "8px 8px 8px 8px", + }, + }} + /> {/* INVESTASI */} diff --git a/src/app_modules/home/component/body_home.tsx b/src/app_modules/home/component/body_home.tsx index 9535b5eb..41ebcb52 100644 --- a/src/app_modules/home/component/body_home.tsx +++ b/src/app_modules/home/component/body_home.tsx @@ -26,6 +26,7 @@ export default function BodyHome() { const [dataUser, setDataUser] = useState({}); const [dataJob, setDataJob] = useState([]); const [loadingJob, setLoadingJob] = useState(true); + const [loading, setLoading] = useState(true); useShallowEffect(() => { cekUserLogin(); @@ -59,16 +60,35 @@ export default function BodyHome() { return ( - - - + + */} + + setLoading(false)} + styles={{ + imageWrapper: { + border: `2px solid ${AccentColor.blue}`, + borderRadius: "10px 10px 10px 10px", + }, + image: { + borderRadius: "8px 8px 8px 8px", + }, + }} + /> From 86433eb5891f9e52684ffb5d74fc181596749df0 Mon Sep 17 00:00:00 2001 From: nico Date: Thu, 26 Dec 2024 13:56:30 +0800 Subject: [PATCH 091/595] Save changes before switching branch --- bun.lockb | Bin 716668 -> 716668 bytes src/app_modules/_global/color/color_pallet.ts | 7 +++++-- src/app_modules/auth/login/view.tsx | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/bun.lockb b/bun.lockb index f486e079beb2452bdedf0d7c49d7fd7dbd4d4ed8..d9e86731f88f79a2fd315943001a845fb026577a 100755 GIT binary patch delta 8399 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 F{tJ`$P?`V$ delta 8376 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
- Nomor telepon + Nomor telepon
{ setPhone(val); From 5448c3a00144d8d13399cc3452727d788504aa72 Mon Sep 17 00:00:00 2001 From: nico Date: Thu, 26 Dec 2024 14:57:53 +0800 Subject: [PATCH 092/595] Fix Validasi di bagian Skeleton dan Fontnya --- src/app_modules/auth/login/view.tsx | 2 +- src/app_modules/auth/validasi/skeleton.tsx | 28 +++++++++++++++++----- src/app_modules/auth/validasi/view.tsx | 12 ++++++---- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/app_modules/auth/login/view.tsx b/src/app_modules/auth/login/view.tsx index 8b2d8064..a7f07695 100644 --- a/src/app_modules/auth/login/view.tsx +++ b/src/app_modules/auth/login/view.tsx @@ -67,7 +67,7 @@ export default function Login({ version }: { version: string }) { Nomor telepon { setPhone(val); diff --git a/src/app_modules/auth/validasi/skeleton.tsx b/src/app_modules/auth/validasi/skeleton.tsx index 6006de6a..abb1202c 100644 --- a/src/app_modules/auth/validasi/skeleton.tsx +++ b/src/app_modules/auth/validasi/skeleton.tsx @@ -2,8 +2,27 @@ import { Stack, Skeleton, Group } from "@mantine/core"; export default function Validasi_SkeletonView() { return ( - <> - + + + + + + + + + + {Array.from({ length: 4 }).map((_, i) => ( + + ))} + + + + + + + ); +} +{/* @@ -19,7 +38,4 @@ export default function Validasi_SkeletonView() { - - - ); -} + */} diff --git a/src/app_modules/auth/validasi/view.tsx b/src/app_modules/auth/validasi/view.tsx index 83894734..af88e352 100644 --- a/src/app_modules/auth/validasi/view.tsx +++ b/src/app_modules/auth/validasi/view.tsx @@ -197,8 +197,8 @@ export default function Validasi() { - Masukan 4 digit kode otp - + Masukan 4 digit kode otp + Yang dikirim ke{" "} {" "} @@ -207,12 +207,14 @@ export default function Validasi() {
- { setInputOtp(val); }} @@ -220,7 +222,7 @@ export default function Validasi() {
- + Tidak menerima kode ?{" "} {counter > 0 ? ( From d8f56309b05315ca01f2c3954bd739f029011b50 Mon Sep 17 00:00:00 2001 From: nico Date: Thu, 26 Dec 2024 17:19:57 +0800 Subject: [PATCH 093/595] Fix dibagian auth dan profile --- src/app_modules/_global/color/color_pallet.ts | 3 +- .../_global/component/box_information.tsx | 4 +- .../component/comp_box_upload_image.tsx | 2 + .../_global/ui/ui_header_tamplate.tsx | 2 +- src/app_modules/auth/login/view.tsx | 2 +- src/app_modules/auth/logout/view.tsx | 5 +- src/app_modules/auth/register/view.tsx | 4 +- .../katalog/component/drawer_katalog_new.tsx | 20 +++--- .../katalog/portofolio/create/view.tsx | 72 +++++++++++++++---- .../_component/comp_load_background.tsx | 5 +- src/app_modules/katalog/profile/edit/view.tsx | 24 +++++-- .../katalog/ui/list_portolio_new.tsx | 6 +- src/app_modules/katalog/ui/profile_detail.tsx | 22 +++--- .../katalog/ui/skeleton_profile.tsx | 30 ++++---- src/app_modules/map/ui/ui_create_pin.tsx | 12 +++- 15 files changed, 142 insertions(+), 71 deletions(-) diff --git a/src/app_modules/_global/color/color_pallet.ts b/src/app_modules/_global/color/color_pallet.ts index 11826c62..22efe4dd 100644 --- a/src/app_modules/_global/color/color_pallet.ts +++ b/src/app_modules/_global/color/color_pallet.ts @@ -2,7 +2,8 @@ export const MainColor = { black: "#202020", darkblue: "#001D3D", yellow: "#E1B525", - white: "#D4D0D0" + white: "#D4D0D0", + red: "#C74E4E" }; export const AccentColor = { diff --git a/src/app_modules/_global/component/box_information.tsx b/src/app_modules/_global/component/box_information.tsx index 5f3f0643..188167b7 100644 --- a/src/app_modules/_global/component/box_information.tsx +++ b/src/app_modules/_global/component/box_information.tsx @@ -31,7 +31,7 @@ export default function ComponentGlobal_BoxInformation({ > * Report - + {informasi} @@ -39,7 +39,7 @@ export default function ComponentGlobal_BoxInformation({ *{" "} - + {informasi} diff --git a/src/app_modules/_global/component/comp_box_upload_image.tsx b/src/app_modules/_global/component/comp_box_upload_image.tsx index 97b05beb..1deef182 100644 --- a/src/app_modules/_global/component/comp_box_upload_image.tsx +++ b/src/app_modules/_global/component/comp_box_upload_image.tsx @@ -1,5 +1,6 @@ import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; import { Box } from "@mantine/core"; +import { MainColor } from "../color"; /** * @@ -28,6 +29,7 @@ export function ComponentGlobal_BoxUploadImage({ height: "100%", borderStyle: "dashed", borderRadius: "5px", + borderColor: MainColor.white, }} > {children} diff --git a/src/app_modules/_global/ui/ui_header_tamplate.tsx b/src/app_modules/_global/ui/ui_header_tamplate.tsx index 6f6425e9..5b76731c 100644 --- a/src/app_modules/_global/ui/ui_header_tamplate.tsx +++ b/src/app_modules/_global/ui/ui_header_tamplate.tsx @@ -67,7 +67,7 @@ export default function UIGlobal_LayoutHeaderTamplate({ customButtonLeft ) : ( { diff --git a/src/app_modules/auth/login/view.tsx b/src/app_modules/auth/login/view.tsx index a7f07695..c536419b 100644 --- a/src/app_modules/auth/login/view.tsx +++ b/src/app_modules/auth/login/view.tsx @@ -99,7 +99,7 @@ export default function Login({ version }: { version: string }) {
- + v {version} diff --git a/src/app_modules/auth/logout/view.tsx b/src/app_modules/auth/logout/view.tsx index 04346acd..06f374f2 100644 --- a/src/app_modules/auth/logout/view.tsx +++ b/src/app_modules/auth/logout/view.tsx @@ -10,6 +10,7 @@ import { useRouter } from "next/navigation"; import { useState } from "react"; import { auth_Logout } from "../fun/fun_logout"; import { RouterAuth } from "@/app/lib/router_hipmi/router_auth"; +import { MainColor } from "@/app_modules/_global/color"; export default function Component_ButtonLogout({userId}: {userId: string}) { const router = useRouter(); @@ -70,9 +71,9 @@ export default function Component_ButtonLogout({userId}: {userId: string}) { setOpened(true); }} > - + - + Keluar
diff --git a/src/app_modules/auth/register/view.tsx b/src/app_modules/auth/register/view.tsx index 8e6f84b3..2ec0987c 100644 --- a/src/app_modules/auth/register/view.tsx +++ b/src/app_modules/auth/register/view.tsx @@ -96,10 +96,10 @@ export default function Register() { REGISTRASI - + - + Anda akan terdaftar dengan nomor berikut{" "} +{nomor} diff --git a/src/app_modules/katalog/component/drawer_katalog_new.tsx b/src/app_modules/katalog/component/drawer_katalog_new.tsx index ab0ef95c..c25cdaa1 100644 --- a/src/app_modules/katalog/component/drawer_katalog_new.tsx +++ b/src/app_modules/katalog/component/drawer_katalog_new.tsx @@ -3,7 +3,7 @@ import { RouterPortofolio, RouterProfile, } from "@/app/lib/router_hipmi/router_katalog"; -import { AccentColor } from "@/app_modules/_global/color"; +import { AccentColor, MainColor } from "@/app_modules/_global/color"; import { gs_admin_navbar_menu, gs_admin_navbar_subMenu, @@ -48,25 +48,25 @@ export default function DrawerKatalogNew({ { id: "1", name: "Edit profile", - icon: , + icon: , path: RouterProfile.edit + param.id, }, { id: "2", name: "Ubah foto profile", - icon: , + icon: , path: RouterProfile.update_foto_profile + param.id, }, { id: "3", name: "Ubah latar belakang", - icon: , + icon: , path: RouterProfile.update_foto_background + param.id, }, { id: "4", name: "Tambah portofolio", - icon: , + icon: , path: RouterPortofolio.create + param.id, }, ]; @@ -95,7 +95,7 @@ export default function DrawerKatalogNew({ borderRight: `1px solid ${AccentColor.blue}`, borderLeft: `1px solid ${AccentColor.blue}`, borderRadius: "20px 20px 0px 0px", - color: "white", + color: MainColor.white, paddingBottom: "5%", }, }} @@ -103,7 +103,7 @@ export default function DrawerKatalogNew({ - + @@ -113,10 +113,10 @@ export default function DrawerKatalogNew({ router.push(e.path, { scroll: false }); }} > - + {e.icon} - + {e.name} @@ -136,7 +136,7 @@ export default function DrawerKatalogNew({ }); }} > - + Dashboard Admin diff --git a/src/app_modules/katalog/portofolio/create/view.tsx b/src/app_modules/katalog/portofolio/create/view.tsx index da4d2719..b8fd5489 100644 --- a/src/app_modules/katalog/portofolio/create/view.tsx +++ b/src/app_modules/katalog/portofolio/create/view.tsx @@ -67,8 +67,14 @@ export default function CreatePortofolio({