Merge pull request #263 from bipproduction/bagas/31-jan-25

fix event admin
This commit is contained in:
Bagasbanuna02
2025-02-03 14:08:14 +08:00
committed by GitHub
13 changed files with 331 additions and 160 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -28,7 +28,7 @@
"@mantine/next": "^6.0.17", "@mantine/next": "^6.0.17",
"@mantine/notifications": "^6.0.17", "@mantine/notifications": "^6.0.17",
"@mantine/tiptap": "^7.5.3", "@mantine/tiptap": "^7.5.3",
"@prisma/client": "^5.0.0", "@prisma/client": "^6.3.0",
"@react-pdf/renderer": "^3.4.4", "@react-pdf/renderer": "^3.4.4",
"@tabler/icons-react": "^2.38.0", "@tabler/icons-react": "^2.38.0",
"@tiptap/extension-highlight": "^2.2.3", "@tiptap/extension-highlight": "^2.2.3",
@@ -72,7 +72,7 @@
"p-limit": "^6.2.0", "p-limit": "^6.2.0",
"pdfjs-dist": "^4.6.82", "pdfjs-dist": "^4.6.82",
"postcss": "8.4.27", "postcss": "8.4.27",
"prisma": "^5.19.1", "prisma": "^6.3.0",
"react": "18.2.0", "react": "18.2.0",
"react-countdown": "^2.3.5", "react-countdown": "^2.3.5",
"react-dom": "18.2.0", "react-dom": "18.2.0",
@@ -90,6 +90,7 @@
"react-toastify": "^9.1.3", "react-toastify": "^9.1.3",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"socket.io-client": "^4.7.2", "socket.io-client": "^4.7.2",
"swr": "^2.3.0",
"tailwindcss": "3.3.3", "tailwindcss": "3.3.3",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "5.1.6", "typescript": "5.1.6",

View File

@@ -2,8 +2,9 @@
// learn more about it in the docs: https://pris.ly/d/prisma-schema // learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
engineType = "binary" engineType = "binary"
binaryTargets = ["native"]
} }
datasource db { datasource db {

View File

@@ -85,7 +85,6 @@ export async function GET(
}, },
}); });
await prisma.$disconnect();
return NextResponse.json({ return NextResponse.json({
success: true, success: true,
message: "Success create sponsor", message: "Success create sponsor",
@@ -93,7 +92,6 @@ export async function GET(
}); });
} catch (error) { } catch (error) {
backendLogger.error("Error get sponsor event", error); backendLogger.error("Error get sponsor event", error);
await prisma.$disconnect();
return NextResponse.json( return NextResponse.json(
{ {
success: false, success: false,
@@ -102,5 +100,7 @@ export async function GET(
}, },
{ status: 500 } { status: 500 }
); );
} finally {
await prisma.$disconnect();
} }
} }

View File

@@ -0,0 +1,39 @@
import { prisma } from "@/app/lib";
import backendLogger from "@/util/backendLogger";
import { NextResponse } from "next/server";
export async function GET(request: Request) {
const method = request.method;
if (method !== "GET") {
return NextResponse.json(
{ success: false, message: "Method not allowed" },
{ status: 405 }
);
}
try {
const data = await prisma.eventMaster_TipeAcara.findMany({
orderBy: {
id: "asc",
},
});
return NextResponse.json({
success: true,
message: "Success get tipe acara",
data: data,
});
} catch (error) {
backendLogger.error("Error get tipe acara", error);
return NextResponse.json(
{
success: false,
message: "Failed get tipe acara ",
reason: (error as Error).message,
},
{ status: 500 }
);
} finally {
await prisma.$disconnect();
}
}

View File

@@ -1,33 +1,55 @@
import { decrypt } from "@/app/auth/_lib/decrypt"; import { decrypt } from "@/app/auth/_lib/decrypt";
import { prisma } from "@/app/lib"; import { prisma } from "@/app/lib";
import { cookies } from 'next/headers' import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
export const dynamic = "force-dynamic"; export const dynamic = "force-dynamic";
export async function GET(req: NextRequest) { export async function GET(req: NextRequest) {
const token = req.headers.get('Authorization')?.split(' ')[1]; try {
const token = req.headers.get("Authorization")?.split(" ")[1];
const decripted = await decrypt({ const decripted = await decrypt({
token: token!, token: token!,
encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY! encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!,
}) });
if (!decripted) { if (!decripted) {
return NextResponse.json({ await prisma.$disconnect();
success: false, return NextResponse.json(
message: "Unauthorized" {
}, { status: 401 }) success: false,
message: "Unauthorized",
},
{ status: 401 }
);
} }
const user = await prisma.user.findUnique({ const user = await prisma.user.findUnique({
where: { where: {
id: decripted.id id: decripted.id,
} },
}) });
// Disconnect after successful query
await prisma.$disconnect();
return NextResponse.json({ return NextResponse.json({
success: true, success: true,
message: "Berhasil mendapatkan data", message: "Berhasil mendapatkan data",
data: user data: user,
}) });
} catch (error) {
// Ensure connection is closed even if error occurs
await prisma.$disconnect();
console.error("Error in user validation:", error);
return NextResponse.json(
{
success: false,
message: "Terjadi kesalahan pada server",
},
{ status: 500 }
);
}
} }

View File

@@ -4,6 +4,7 @@ export {
apiGetAdminEventRiwayatCount as apiGetEventRiwayatCount, apiGetAdminEventRiwayatCount as apiGetEventRiwayatCount,
apiGetAdminEventByStatus as apiGetDataEventByStatus, apiGetAdminEventByStatus as apiGetDataEventByStatus,
apiGetAdminEventRiwayat, apiGetAdminEventRiwayat,
apiGetAdminEventTipeAcara,
}; };
const apiGetAdminEventStatusCountDashboard = async ({ const apiGetAdminEventStatusCountDashboard = async ({
@@ -114,3 +115,20 @@ const apiGetAdminEventRiwayat = async ({
return await response.json().catch(() => null); return await response.json().catch(() => null);
}; };
const apiGetAdminEventTipeAcara = async () => {
const { token } = await fetch("/api/get-cookie").then((res) => res.json());
if (!token) return await token.json().catch(() => null);
const response = await fetch(`/api/event/tipe-acara`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
});
return await response.json().catch(() => null);
};

View File

@@ -1,8 +1,6 @@
import { AdminEvent_Riwayat } from "@/app_modules/admin/event"; import { AdminEvent_Riwayat } from "@/app_modules/admin/event";
import { adminEvent_funGetListAllRiwayat } from "@/app_modules/admin/event/fun/get/get_list_all_riwayat";
export default async function Page() { export default async function Page() {
// const listRiwayat = await adminEvent_funGetListAllRiwayat({ page: 1 });
return ( return (
<> <>

View File

@@ -2,11 +2,11 @@ import { AdminEvent_DetailTipeAcara } from "@/app_modules/admin/event";
import { AdminEvent_getListTipeAcara } from "@/app_modules/admin/event/fun/get/get_list_tipe_acara"; import { AdminEvent_getListTipeAcara } from "@/app_modules/admin/event/fun/get/get_list_tipe_acara";
export default async function Page() { export default async function Page() {
const listTipe = await AdminEvent_getListTipeAcara() // const listTipe = await AdminEvent_getListTipeAcara()
return ( return (
<> <>
<AdminEvent_DetailTipeAcara listTipe={listTipe}/> <AdminEvent_DetailTipeAcara />
</> </>
); );
} }

View File

@@ -0,0 +1,30 @@
"use client";
import { Stack } from "@mantine/core";
import useSwr from "swr";
const fether = (url: string) =>
fetch("https://jsonplaceholder.typicode.com" + url, {
cache: "force-cache",
next: {
revalidate: 60,
},
}).then((res) => res.json());
export default function LoadDataContoh() {
const { data, isLoading, error, mutate, isValidating } = useSwr(
"/posts/1",
fether,
{
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshInterval: 1000,
}
);
return (
<Stack>
{isLoading && <div>Loading...</div>}
LoadDataContoh
{JSON.stringify(data, null, 2)}
</Stack>
);
}

View File

@@ -0,0 +1,9 @@
async function getDataExample() {
const res = await fetch("https://jsonplaceholder.typicode.com/posts", {
next: {
revalidate: 60,
},
});
return res.json();
}

View File

@@ -1,14 +1,51 @@
"use client"; import { Suspense } from "react";
import { useHookstate } from "@hookstate/core"; import LoadDataContoh from "./LoadDataContoh";
import { Button, Stack } from "@mantine/core";
const listMenu = [
{
name: "Dashboard",
url: "/dashboard",
icon: "dashboard",
},
{
name: "Event",
url: "/event",
icon: "event",
},
{
name: "Donasi",
url: "/donasi",
icon: "donasi",
},
];
export default function Page() { const fether = async (url: string) =>
fetch("https://jsonplaceholder.typicode.com" + url, {
next: {
revalidate: 2,
},
}).then(async (res) => {
const data = await res.json();
// console.log(data);
return data;
});
export default async function Page() {
const data = await fether("/posts/1");
return ( return (
<Stack> <div>
{listMenu.map((item) => {
<Button onClick={() => {}}>tekan</Button> return (
</Stack> <div key={item.name}>
<a href={item.url}>{item.name}</a>
</div>
);
})}
{/* <LoadDataContoh /> */}
<Suspense fallback={<div>Loading...</div>}>
{JSON.stringify(data, null, 2)}
</Suspense>
</div>
); );
} }

View File

@@ -17,7 +17,7 @@ import {
TextInput, TextInput,
Title, Title,
} from "@mantine/core"; } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks"; import { useDisclosure, useShallowEffect } from "@mantine/hooks";
import { IconCirclePlus, IconEditCircle, IconTrash } from "@tabler/icons-react"; import { IconCirclePlus, IconEditCircle, IconTrash } from "@tabler/icons-react";
import { useState } from "react"; import { useState } from "react";
import ComponentAdminGlobal_HeaderTamplate from "../../_admin_global/header_tamplate"; import ComponentAdminGlobal_HeaderTamplate from "../../_admin_global/header_tamplate";
@@ -25,29 +25,23 @@ import { AdminEvent_funCreateTipeAcara } from "../fun/create/fun_create_tipe_aca
import { AdminEvent_funEditActivationTipeAcaraById } from "../fun/edit/fun_edit_activation_tipe_acara"; import { AdminEvent_funEditActivationTipeAcaraById } from "../fun/edit/fun_edit_activation_tipe_acara";
import { AdminEvent_funEditTipeAcara } from "../fun/edit/fun_edit_tipe_acara"; import { AdminEvent_funEditTipeAcara } from "../fun/edit/fun_edit_tipe_acara";
import { AdminEvent_getListTipeAcara } from "../fun/get/get_list_tipe_acara"; import { AdminEvent_getListTipeAcara } from "../fun/get/get_list_tipe_acara";
import { apiGetAdminEventTipeAcara } from "@/app/dev/admin/event/_lib/api_fecth_admin_event";
import CustomSkeleton from "@/app_modules/components/CustomSkeleton";
import { clientLogger } from "@/util/clientLogger";
export default function AdminEvent_DetailTipeAcara({ export default function AdminEvent_DetailTipeAcara() {
listTipe,
}: {
listTipe: any;
}) {
return ( return (
<> <>
<Stack> <Stack>
<ComponentAdminGlobal_HeaderTamplate name="Event" /> <ComponentAdminGlobal_HeaderTamplate name="Event" />
<DetailTipeAcara />
<DetailTipeAcara listTipe={listTipe} />
</Stack> </Stack>
</> </>
); );
} }
function DetailTipeAcara({ function DetailTipeAcara() {
listTipe, const [tipe, setTipe] = useState<MODEL_DEFAULT_MASTER_OLD[] | null>(null);
}: {
listTipe: MODEL_DEFAULT_MASTER_OLD[];
}) {
const [tipe, setTipe] = useState(listTipe);
const [name, setName] = useState(""); const [name, setName] = useState("");
const [openEditor, setOpenEditor] = useState(false); const [openEditor, setOpenEditor] = useState(false);
const [edit, setEdit] = useState<MODEL_DEFAULT_MASTER_OLD | null>(null); const [edit, setEdit] = useState<MODEL_DEFAULT_MASTER_OLD | null>(null);
@@ -58,6 +52,21 @@ function DetailTipeAcara({
}); });
const [openCreate, setOpenCreate] = useState(false); const [openCreate, setOpenCreate] = useState(false);
useShallowEffect(() => {
onLoadData();
}, []);
async function onLoadData() {
try {
const respone = await apiGetAdminEventTipeAcara();
if (respone) {
setTipe(respone.data);
}
} catch (error) {
clientLogger.error("Error get tipe acara", error);
}
}
return ( return (
<> <>
<Modal opened={opened} onClose={close} centered withCloseButton={false}> <Modal opened={opened} onClose={close} centered withCloseButton={false}>
@@ -101,135 +110,142 @@ function DetailTipeAcara({
</Button> </Button>
</Group> </Group>
<SimpleGrid {!tipe ? (
cols={2} <CustomSkeleton height={400} width={"60%"} />
spacing="lg" ) : (
breakpoints={[ <SimpleGrid
{ maxWidth: "62rem", cols: 4, spacing: "lg" }, cols={2}
{ maxWidth: "48rem", cols: 2, spacing: "sm" }, spacing="lg"
{ maxWidth: "36rem", cols: 1, spacing: "sm" }, breakpoints={[
]} { maxWidth: "62rem", cols: 4, spacing: "lg" },
> { maxWidth: "48rem", cols: 2, spacing: "sm" },
<div> { maxWidth: "36rem", cols: 1, spacing: "sm" },
<Paper p={"md"} shadow="lg" withBorder> ]}
<Stack> >
<Title order={3}>Tipe Acara Yang Tersedia </Title>
<Stack px={"md"}>
{tipe.map((e, i) => (
<Stack key={e.id} spacing={"xs"}>
<Group position="apart">
<Text>{e.name}</Text>
<Group>
<ActionIcon
variant="transparent"
onClick={() => {
setOpenEditor(true);
setOpenCreate(false);
setEdit(e);
}}
>
<IconEditCircle color="green" />
</ActionIcon>{" "}
<ActionIcon
variant="transparent"
onClick={() => {
open();
setHapusTipe({
...hapusTipe,
id: e.id,
name: e.name,
});
}}
>
<IconTrash color="red" />
</ActionIcon>
</Group>
</Group>
<Divider />
</Stack>
))}
</Stack>
</Stack>
</Paper>
</div>
{openCreate ? (
<div> <div>
<Paper p={"sm"} shadow="lg" withBorder> <Paper p={"md"} shadow="lg" withBorder>
<Stack> <Stack>
<TextInput <Title order={3}>Tipe Acara Yang Tersedia </Title>
value={name ? name : ""} <Stack px={"md"}>
label="Masukan Tipe" {tipe.map((e, i) => (
placeholder="Contoh: Seminar, Workshop, dll." <Stack key={e.id} spacing={"xs"}>
onChange={(val) => { <Group position="apart">
setName(val.currentTarget.value); <Text>{e.name}</Text>
}} <Group>
/> <ActionIcon
<Group position="right"> variant="transparent"
<Button radius={"xl"} onClick={() => setOpenCreate(false)}> onClick={() => {
Batal setOpenEditor(true);
</Button> setOpenCreate(false);
<Button setEdit(e);
disabled={!name} }}
style={{ >
transition: "all 0.5s ease", <IconEditCircle color="green" />
}} </ActionIcon>{" "}
color="green" <ActionIcon
radius={"xl"} variant="transparent"
onClick={() => onSave(name, setName, setTipe)} onClick={() => {
> open();
Simpan setHapusTipe({
</Button> ...hapusTipe,
</Group> id: e.id,
name: e.name,
});
}}
>
<IconTrash color="red" />
</ActionIcon>
</Group>
</Group>
<Divider />
</Stack>
))}
</Stack>
</Stack> </Stack>
</Paper> </Paper>
</div> </div>
) : (
""
)}
<div> {openCreate ? (
{openEditor ? ( <div>
<Paper p={"sm"} shadow="lg" withBorder> <Paper p={"sm"} shadow="lg" withBorder>
<Stack> <Stack>
<TextInput <TextInput
value={edit?.name ? edit?.name : ""} value={name ? name : ""}
label="Edit Tipe" label="Masukan Tipe"
placeholder="Contoh: Ramah Tamah, dll" placeholder="Contoh: Seminar, Workshop, dll."
onChange={(val) => { onChange={(val) => {
setEdit({ setName(val.currentTarget.value);
...(edit as any), }}
namaBank: val.target.value, />
}); <Group position="right">
}} <Button radius={"xl"} onClick={() => setOpenCreate(false)}>
/>
<Group position="right">
<Group position="apart">
<Button radius={"xl"} onClick={() => setOpenEditor(false)}>
Batal Batal
</Button> </Button>
<Button <Button
disabled={!edit?.name} disabled={!name}
style={{ style={{
transition: "all 0.5s ease", transition: "all 0.5s ease",
}} }}
radius={"xl"}
color="green" color="green"
onClick={() => radius={"xl"}
onUpdate(edit?.id, edit?.name, setTipe, setOpenEditor) onClick={() => onSave(name, setName, setTipe)}
}
> >
Update Simpan
</Button> </Button>
</Group> </Group>
</Group> </Stack>
</Stack> </Paper>
</Paper> </div>
) : ( ) : (
"" ""
)} )}
</div>
</SimpleGrid> <div>
{openEditor ? (
<Paper p={"sm"} shadow="lg" withBorder>
<Stack>
<TextInput
value={edit?.name ? edit?.name : ""}
label="Edit Tipe"
placeholder="Contoh: Ramah Tamah, dll"
onChange={(val) => {
setEdit({
...(edit as any),
namaBank: val.target.value,
});
}}
/>
<Group position="right">
<Group position="apart">
<Button
radius={"xl"}
onClick={() => setOpenEditor(false)}
>
Batal
</Button>
<Button
disabled={!edit?.name}
style={{
transition: "all 0.5s ease",
}}
radius={"xl"}
color="green"
onClick={() =>
onUpdate(edit?.id, edit?.name, setTipe, setOpenEditor)
}
>
Update
</Button>
</Group>
</Group>
</Stack>
</Paper>
) : (
""
)}
</div>
</SimpleGrid>
)}
</> </>
); );
} }