API Dashboard Admin

This commit is contained in:
2025-01-30 14:40:16 +08:00
parent 22fa1e8bcd
commit d539678c1b
10 changed files with 292 additions and 35 deletions

View File

@@ -0,0 +1,29 @@
import { prisma } from "@/app/lib";
import backendLogger from "@/util/backendLogger";
import { NextResponse } from "next/server";
export async function GET(request: Request) {
try {
const data = await prisma.investasi.count({
where: {
active: true
},
})
return NextResponse.json({
message: "Data Investasi",
data: data,
},
{ status: 200 }
)
} catch (error) {
backendLogger.error("Error Get Count Investasi Main Dashboard")
return NextResponse.json({
message: "Error Get Count Investasi Main Dashboard",
reason: (error as Error).message
},
{ status: 500 }
)
} finally {
await prisma.$disconnect();
}
}

View File

@@ -0,0 +1,31 @@
import { prisma } from "@/app/lib";
import backendLogger from "@/util/backendLogger";
import { NextResponse } from "next/server";
export async function GET(request: Request) {
try {
const data = await prisma.portofolio.count({
where: {
active: true
}
});
return NextResponse.json({
success: true,
message: "Data portofolio",
data: data
},
{ status: 200 }
);
} catch (error) {
backendLogger.error("Error Get Count Portofolio Main Dashboard")
return NextResponse.json({
success: false,
message: "Error Get Count Portofolio Main Dashboard",
data: null
},
{ status: 500 }
);
} finally {
await prisma.$disconnect();
}
}

View File

@@ -0,0 +1,42 @@
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.user.count({
where: {
active: true
},
})
return NextResponse.json({
success: true,
message: "Data user",
data: data
},
{ status: 200 }
)
} catch (error) {
backendLogger.error("Error Get Count User Main Dashboard")
return NextResponse.json({
success: false,
message: "Gagal mendapatkan data",
reason: (error as Error).message
},
{ status: 500 }
)
} finally {
await prisma.$disconnect();
}
}

View File

@@ -33,7 +33,6 @@ export async function GET(
});
await prisma.$disconnect();
return NextResponse.json({
success: true,
message: "Berhasil mendapatkan data",
@@ -41,7 +40,6 @@ export async function GET(
});
} catch (error) {
await prisma.$disconnect();
return NextResponse.json(
{ success: false, message: "Gagal mendapatkan data" },
{ status: 500 }

View File

@@ -3,12 +3,12 @@ import { AdminMainDashboard_CountPOrtofolio } from "@/app_modules/admin/main_das
import { AdminMainDashboard_CountUser } from "@/app_modules/admin/main_dashboard/fun/count/fun_count_user";
export default async function Page() {
const countUser = await AdminMainDashboard_CountUser();
const countPorto = await AdminMainDashboard_CountPOrtofolio();
// const countUser = await AdminMainDashboard_CountUser();
// const countPorto = await AdminMainDashboard_CountPOrtofolio();
// await new Promise((a, b) => {
// setTimeout(a, 4000);
// });
return <AdminMain countUser={countUser} countPorto={countPorto} />;
return <AdminMain/>;
}

View File

@@ -0,0 +1,34 @@
import { Skeleton, SkeletonProps, createStyles } from '@mantine/core';
interface CustomSkeletonProps extends SkeletonProps {
isLoading?: boolean;
className?: string;
}
const useStyles = createStyles((theme) => ({
skeleton: {
'&::before': {
backgroundColor: "#1F5B9E",
},
'&::after': {
backgroundColor: "#0F3055",
},
},
}));
const CustomSkeletonAdmin: React.FC<CustomSkeletonProps> = ({
isLoading = true,
className,
...props
}) => {
const { classes, cx } = useStyles();
return (
<Skeleton
className={cx(classes.skeleton, className)}
visible={isLoading}
{...props}
/>
);
};
export default CustomSkeletonAdmin;

View File

@@ -0,0 +1,53 @@
import { MainColor, AccentColor } from '@/app_modules/_global/color';
import { AdminColor } from '@/app_modules/_global/color/color_pallet';
import { Flex, Grid, Paper, Stack, Text, ThemeIcon, Title } from '@mantine/core';
import React from 'react';
import CustomSkeletonAdmin from './customSkeletonAdmin';
import ComponentAdminGlobal_HeaderTamplate from '../../header_tamplate';
import { IconFileText, IconUsers } from '@tabler/icons-react';
function MainDashboardSkeleton() {
const listBox = [
{
id: 1,
name: "User",
jumlah: "",
link: "",
icon: <IconUsers size={18} color="#0066CCFF" />
},
{
id: 2,
name: "Portofolio",
jumlah: "countPortofolio",
link: "",
icon: <IconFileText size={18} color={"#B6A22EFF"} />
},
];
return (
<>
<Stack spacing={"sm"}>
<ComponentAdminGlobal_HeaderTamplate name="Main Dashboard" />
<Grid>
{listBox.map((e) => (
<Grid.Col md={4} lg={4} key={e.id}>
<Paper style={{ borderColor: "transparent" }} bg={AdminColor.softBlue} withBorder shadow="md" radius="md" p="md">
<Stack spacing={0}>
<Text fw={"bold"} c={MainColor.white}>{e.name}</Text>
<Flex align={"center"} justify={"space-between"}>
<CustomSkeletonAdmin w={40} h={50} />
<ThemeIcon radius={"xl"} size={"md"} color={AccentColor.white}>{e.icon}</ThemeIcon>
</Flex>
</Stack>
</Paper>
</Grid.Col>
))}
<Grid.Col md={4} lg={4}>
{/* <PieChart /> */}
</Grid.Col>
</Grid>
</Stack>
</>
);
}
export default MainDashboardSkeleton;

View File

@@ -0,0 +1,30 @@
export const apiGetCountUserActive = async () => {
const { token } = await fetch("/api/get-cookie").then((res) => res.json());
if (!token) return await token.json().catch(() => null);
const response = await fetch(`/api/admin/main_dashboard/user`, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
});
return await response.json().catch(() => null);
}
export const apiGetCountPortofolio = async () => {
const { token } = await fetch("/api/get-cookie").then((res) => res.json());
if (!token) return await token.json().catch(() => null);
const response = await fetch(`/api/admin/main_dashboard/portofolio`, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
Authorization: `Bearer ${token}`,
},
});
return await response.json().catch(() => null);
}

View File

@@ -4,14 +4,50 @@ import { AccentColor, AdminColor, MainColor } from "@/app_modules/_global/color/
import { Divider, Flex, Grid, Group, Paper, Stack, Text, ThemeIcon, Title } from "@mantine/core";
import { IconFileText, IconUsers } from "@tabler/icons-react";
import ComponentAdminGlobal_HeaderTamplate from "../../_admin_global/header_tamplate";
import { useState } from "react";
import { useShallowEffect } from "@mantine/hooks";
import { apiGetCountPortofolio, apiGetCountUserActive } from "../lib/api_fetch_main_dashboard";
import { NextResponse } from "next/server";
import { clientLogger } from "@/util/clientLogger";
import MainDashboardSkeleton from "../../_admin_global/_component/skeleton/main_dashboard_skeleton";
export default function AdminMain() {
const [countUser, setCountUser] = useState<number | null>(null);
const [countPortofolio, setCountPortofolio] = useState<number | null>(null);
useShallowEffect(() => {
onLoadDataUser();
onLoadDataPortofolio();
}, []);
async function onLoadDataUser() {
try {
const response = await apiGetCountUserActive();
if (response) {
// console.log(response.data);
// console.log(typeof response.data);
// console.log( response);
setCountUser(response.data);
}
} catch (error) {
clientLogger.error("Error get count user data", error);
}
}
async function onLoadDataPortofolio() {
try {
const response = await apiGetCountPortofolio();
if (response) {
// console.log("Response Portofolio",response);
setCountPortofolio(response.data);
}
} catch (error) {
clientLogger.error("Error get count portofolio data", error);
}
}
export default function AdminMain({
countUser,
countPorto,
}: {
countUser: number;
countPorto: number;
}) {
const listBox = [
{
id: 1,
@@ -23,39 +59,44 @@ export default function AdminMain({
{
id: 2,
name: "Portofolio",
jumlah: countPorto,
jumlah: countPortofolio,
link: "",
icon: <IconFileText size={18} color={"#B6A22EFF"}/>
icon: <IconFileText size={18} color={"#B6A22EFF"} />
},
];
return (
<>
<Stack spacing={"sm"}>
<ComponentAdminGlobal_HeaderTamplate name="Main Dashboard"/>
{/* <Title c={AdminColor.white}>Main Dashboard</Title>
{!countUser && !countPortofolio ? (
<MainDashboardSkeleton />
) : (
<Stack spacing={"sm"}>
<ComponentAdminGlobal_HeaderTamplate name="Main Dashboard" />
{/* <Title c={AdminColor.white}>Main Dashboard</Title>
<Divider c={AdminColor.dividerWhite} mb={"md"} size={"xs"} /> */}
<Grid>
{listBox.map((e) => (
<Grid.Col md={4} lg={4} key={e.id}>
<Paper style={{ borderColor: "transparent"}} bg={AdminColor.softBlue} withBorder shadow="md" radius="md" p="md">
<Grid>
{listBox.map((e) => (
<Grid.Col md={4} lg={4} key={e.id}>
<Paper style={{ borderColor: "transparent" }} bg={AdminColor.softBlue} withBorder shadow="md" radius="md" p="md">
<Stack spacing={0}>
<Text fw={"bold"} c={MainColor.white}>{e.name}</Text>
<Flex align={"center"} justify={"space-between"}>
<Title c={MainColor.white}>{e.jumlah}</Title>
<ThemeIcon radius={"xl"} size={"md"} color={AccentColor.white}>{e.icon}</ThemeIcon>
</Flex>
<Text fw={"bold"} c={MainColor.white}>{e.name}</Text>
<Flex align={"center"} justify={"space-between"}>
<Title c={MainColor.white}>{e.jumlah}</Title>
<ThemeIcon radius={"xl"} size={"md"} color={AccentColor.white}>{e.icon}</ThemeIcon>
</Flex>
</Stack>
</Paper>
</Grid.Col>
))}
</Paper>
</Grid.Col>
))}
<Grid.Col md={4} lg={4}>
{/* <PieChart /> */}
</Grid.Col>
</Grid>
</Stack>
<Grid.Col md={4} lg={4}>
{/* <PieChart /> */}
</Grid.Col>
</Grid>
</Stack>
)}
</>
);
}

View File

@@ -1,5 +1,4 @@
import { Skeleton, SkeletonProps, createStyles } from '@mantine/core';
import { AccentColor } from '../_global/color';
interface CustomSkeletonProps extends SkeletonProps {
isLoading?: boolean;