added dashboard data
This commit is contained in:
parent
3f2495f210
commit
07abcc29d2
13 changed files with 469 additions and 20 deletions
|
@ -12,6 +12,9 @@ import Transactions from "pages/transactions/Transactions";
|
|||
import Geography from "pages/geography/Geography";
|
||||
import Overview from "pages/overview/Overview";
|
||||
import Daily from "pages/dailyStat/Daily";
|
||||
import Monthly from "pages/monthlyStat/Monthly";
|
||||
import Breakdown from "pages/breakdown/Breakdown";
|
||||
|
||||
|
||||
|
||||
function App() {
|
||||
|
@ -35,6 +38,8 @@ function App() {
|
|||
<Route path="/geography" element={<Geography/>} />
|
||||
<Route path="/overview" element={<Overview/>} />
|
||||
<Route path="/daily" element={<Daily/>}/>
|
||||
<Route path="/monthly" element={<Monthly/>}/>
|
||||
<Route path="/breakdown" element={<Breakdown/>}/>
|
||||
</Route>
|
||||
</Routes>
|
||||
</ThemeProvider>
|
||||
|
|
124
admin_dashboard/client/src/components/BreakdownChart.jsx
Normal file
124
admin_dashboard/client/src/components/BreakdownChart.jsx
Normal file
|
@ -0,0 +1,124 @@
|
|||
import React from 'react'
|
||||
import { ResponsivePie } from '@nivo/pie'
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import { useGetSalesQuery } from 'state/api';
|
||||
|
||||
|
||||
|
||||
|
||||
const BreakdownChart = ({ isDashboard = false }) => {
|
||||
const theme = useTheme();
|
||||
const {data, isLoading} = useGetSalesQuery();
|
||||
if (!data || isLoading) return "Loading...";
|
||||
|
||||
const colors = [
|
||||
theme.palette.secondary[500],
|
||||
theme.palette.secondary[300],
|
||||
theme.palette.secondary[300],
|
||||
theme.palette.secondary[500],
|
||||
];
|
||||
const formattedData = Object.entries(data.salesByCategory).map(
|
||||
([category, sales], i) => ({
|
||||
id: category,
|
||||
label: category,
|
||||
value: sales,
|
||||
color: colors[i],
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<Box height={isDashboard ? "400px" : "100%"} width={undefined} minHeight={isDashboard ? "325px" : undefined} minWidth={isDashboard ? "325px" : undefined} position="relative">
|
||||
<ResponsivePie data={formattedData}
|
||||
theme={{
|
||||
axis: {
|
||||
domain: {
|
||||
line: {
|
||||
stroke: theme.palette.secondary[200],
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
text: {
|
||||
fill: theme.palette.secondary[200],
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
line: {
|
||||
stroke: theme.palette.secondary[200],
|
||||
strokeWidth: 1,
|
||||
},
|
||||
text: {
|
||||
fill: theme.palette.secondary[200],
|
||||
},
|
||||
},
|
||||
},
|
||||
legends: {
|
||||
text: {
|
||||
fill: theme.palette.secondary[200],
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
container: {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
},
|
||||
}}
|
||||
colors={{ datum: "data.color" }}
|
||||
margin={
|
||||
isDashboard
|
||||
? { top: 40, right: 80, bottom: 100, left: 50 }
|
||||
: { top: 40, right: 80, bottom: 80, left: 80 }
|
||||
}
|
||||
sortByValue={true}
|
||||
innerRadius={0.45}
|
||||
activeOuterRadiusOffset={8}
|
||||
borderWidth={1}
|
||||
borderColor={{
|
||||
from: "color",
|
||||
modifiers: [["darker", 0.2]],
|
||||
}}
|
||||
enableArcLinkLabels={!isDashboard}
|
||||
arcLinkLabelsTextColor={theme.palette.secondary[200]}
|
||||
arcLinkLabelsThickness={2}
|
||||
arcLinkLabelsColor={{ from: "color" }}
|
||||
arcLabelsSkipAngle={10}
|
||||
arcLabelsTextColor={{
|
||||
from: "color",
|
||||
modifiers: [["darker", 2]],
|
||||
}}
|
||||
legends={[
|
||||
{
|
||||
anchor: "bottom",
|
||||
direction: "row",
|
||||
justify: false,
|
||||
translateX: isDashboard ? 20 : 0,
|
||||
translateY: isDashboard ? 50 : 56,
|
||||
itemsSpacing: 0,
|
||||
itemWidth: 85,
|
||||
itemHeight: 18,
|
||||
itemTextColor: "#999",
|
||||
itemDirection: "left-to-right",
|
||||
itemOpacity: 1,
|
||||
symbolSize: 18,
|
||||
symbolShape: "circle",
|
||||
effects: [
|
||||
{
|
||||
on: "hover",
|
||||
style: {
|
||||
itemTextColor: theme.palette.primary[500],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Box position="absolute" top="50%" left="50%" color={theme.palette.secondary[400]} textAlign="center" pointerEvents="none" sx={{transform: isDashboard ? "translate(-75%, -170%)" : "translate(-50%, -100%)",}}>
|
||||
<Typography variant="h6">
|
||||
{!isDashboard && "Total:"} ${data.yearlySalesTotal}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default BreakdownChart
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react'
|
||||
import { Box, Divider, Drawer, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography, useTheme } from '@mui/material'
|
||||
import { SettingsOutlined, ChevronLeft, ChevronRightOutlined, HomeOutlined, Groups2Outlined, ReceiptLongOutlined, PublicOutlined,
|
||||
TodayOutlined, CalendarMonthOutlined, AdminPanelSettingsOutlined, TrendingUpOutlined, PieChartOutlined } from '@mui/icons-material'
|
||||
TodayOutlined, CalendarMonthOutlined, PieChartOutlined } from '@mui/icons-material'
|
||||
import CategoryOutlinedIcon from '@mui/icons-material/CategoryOutlined';
|
||||
import PlagiarismOutlinedIcon from '@mui/icons-material/PlagiarismOutlined';
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
|
@ -54,18 +54,6 @@ const navItems = [
|
|||
{
|
||||
text: "Breakdown",
|
||||
icon: <PieChartOutlined />
|
||||
},
|
||||
{
|
||||
text: "Management",
|
||||
icon: null,
|
||||
},
|
||||
{
|
||||
text: "Admin",
|
||||
icon: <AdminPanelSettingsOutlined />
|
||||
},
|
||||
{
|
||||
text: "Performance",
|
||||
icon: <TrendingUpOutlined />
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -95,8 +83,8 @@ const Sidebar = ({user, drawerWidth, sidebarOpen, setSidebarOpen, nonMobile,}) =
|
|||
<Box m="1.5rem 2rem 2rem 3rem">
|
||||
<FlexBetween color={theme.palette.secondary.main}>
|
||||
<Box display="flex" alignItems="center" gap="0.5rem">
|
||||
<Typography variant='h4' fontWeight="bold" >
|
||||
Jtp Dashboard
|
||||
<Typography variant='h5' fontWeight="bold">
|
||||
Analytics Dashboard
|
||||
</Typography>
|
||||
</Box>
|
||||
{!nonMobile && (
|
||||
|
|
24
admin_dashboard/client/src/components/StatBox.jsx
Normal file
24
admin_dashboard/client/src/components/StatBox.jsx
Normal file
|
@ -0,0 +1,24 @@
|
|||
import React from 'react'
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
import FlexBetween from "./FlexBetween";
|
||||
|
||||
|
||||
const StatBox = ({ title, value, increase, icon, description }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
|
||||
return (
|
||||
<Box gridColumn="span 2" gridRow="span 1" display="flex" flexDirection="column" justifyContent="space-between" p="1.25rem 1rem" flex="1 1 100%" backgroundColor={theme.palette.background.alt} borderRadius="0.55rem">
|
||||
<FlexBetween>
|
||||
<Typography variant="h6" sx={{ color: theme.palette.secondary[100] }}>{title}</Typography>{icon}
|
||||
</FlexBetween>
|
||||
<Typography variant="h3" fontWeight="600" sx={{ color: theme.palette.secondary[200] }}>{value}</Typography>
|
||||
<FlexBetween gap="1rem">
|
||||
<Typography variant="h5" fontStyle="italic" sx={{ color: theme.palette.secondary.light }}>{increase}</Typography>
|
||||
<Typography>{description}</Typography>
|
||||
</FlexBetween>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default StatBox
|
17
admin_dashboard/client/src/pages/breakdown/Breakdown.jsx
Normal file
17
admin_dashboard/client/src/pages/breakdown/Breakdown.jsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { Box } from '@mui/material'
|
||||
import BreakdownChart from 'components/BreakdownChart'
|
||||
import Header from 'components/Header'
|
||||
import React from 'react'
|
||||
|
||||
const Breakdown = () => {
|
||||
return (
|
||||
<Box m="1.5rem 2.5rem">
|
||||
<Header title="Breakdown" subtitle="Breakdown of Sales By Category"/>
|
||||
<Box mt="40px" height="75vh">
|
||||
<BreakdownChart />
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Breakdown
|
|
@ -1,8 +1,95 @@
|
|||
import { Box, Button, Typography, useTheme, useMediaQuery, } from '@mui/material'
|
||||
import FlexBetween from 'components/FlexBetween'
|
||||
import Header from 'components/Header'
|
||||
import React from 'react'
|
||||
import { DownloadOutlined, Email, PointOfSale, PersonAdd, Traffic } from "@mui/icons-material";
|
||||
import { DataGrid } from '@mui/x-data-grid';
|
||||
import BreakdownChart from 'components/BreakdownChart';
|
||||
import OverviewChart from 'components/OverviewChart';
|
||||
import { useGetDashboardQuery } from 'state/api';
|
||||
import StatBox from 'components/StatBox';
|
||||
|
||||
|
||||
|
||||
|
||||
const Dashboard = () => {
|
||||
const theme = useTheme();
|
||||
const isNonMediumScreens = useMediaQuery("(min-width: 1200px)");
|
||||
const { data, isLoading } = useGetDashboardQuery();
|
||||
console.log(data)
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: "_id",
|
||||
headerName: "ID",
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "userId",
|
||||
headerName: "User ID",
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "createdAt",
|
||||
headerName: "CreatedAt",
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: "products",
|
||||
headerName: "# of Products",
|
||||
flex: 0.5,
|
||||
sortable: false,
|
||||
renderCell: (params) => params.value.length,
|
||||
},
|
||||
{
|
||||
field: "cost",
|
||||
headerName: "Cost",
|
||||
flex: 1,
|
||||
renderCell: (params) => `$${Number(params.value).toFixed(2)}`,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
return (
|
||||
<div>Dashboard</div>
|
||||
<Box m="1.5rem 2.5rem">
|
||||
<FlexBetween>
|
||||
<Header title="Dashboard"/>
|
||||
<Box>
|
||||
<Button sx={{ backgroundColor: theme.palette.secondary.light, color: theme.palette.background.alt, fontSize: "14px", fontWeight: "bold", padding: "10px 20px", }}>
|
||||
<DownloadOutlined sx={{ mr: "10px" }} /> Download Reports
|
||||
</Button>
|
||||
</Box>
|
||||
</FlexBetween>
|
||||
<Box mt="20px" display="grid" gridTemplateColumns="repeat(12, 1fr)" gridAutoRows="160px" gap="20px" sx={{"& > div": { gridColumn: isNonMediumScreens ? undefined : "span 12" }}}>
|
||||
<StatBox title="Total Customers" value={data && data.totalCustomers} increase="+14%" description="Since last month"
|
||||
icon={<Email sx={{ color: theme.palette.secondary[300], fontSize: "26px" }}/>}/>
|
||||
<StatBox title="Sales Today" value={data && data.todayStats.totalSales} increase="+21%" description="Since last month"
|
||||
icon={<PointOfSale sx={{ color: theme.palette.secondary[300], fontSize: "26px" }}/>}/>
|
||||
<Box gridColumn="span 8" gridRow="span 2" backgroundColor={theme.palette.background.alt} p="1rem" borderRadius="0.55rem">
|
||||
<OverviewChart view="sales" isDashboard={true} />
|
||||
</Box>
|
||||
<StatBox title="Monthly Sales" value={data && data.thisMonthStats.totalSales} increase="+5%" description="Since last month"
|
||||
icon={<PersonAdd sx={{ color: theme.palette.secondary[300], fontSize: "26px" }}/>}/>
|
||||
<StatBox title="Yearly Sales" value={data && data.yearlySalesTotal} increase="+43%" description="Since last month"
|
||||
icon={<Traffic sx={{ color: theme.palette.secondary[300], fontSize: "26px" }}/>}/>
|
||||
|
||||
<Box gridColumn="span 8" gridRow="span 3" sx={{"& .MuiDataGrid-root": {border: "none", borderRadius: "5rem",},
|
||||
"& .MuiDataGrid-cell": {borderBottom: "none",},
|
||||
"& .MuiDataGrid-columnHeaders": {backgroundColor: theme.palette.background.alt, color: theme.palette.secondary[100], borderBottom: "none",},
|
||||
"& .MuiDataGrid-virtualScroller": {backgroundColor: theme.palette.background.alt,},
|
||||
"& .MuiDataGrid-footerContainer": {backgroundColor: theme.palette.background.alt, color: theme.palette.secondary[100], borderTop: "none",},
|
||||
"& .MuiDataGrid-toolbarContainer .MuiButton-text": {color: `${theme.palette.secondary[200]} !important`,},}}>
|
||||
<DataGrid loading={isLoading || !data} getRowId={(row) => row._id} rows={(data && data.transactions) || []} columns={columns}/>
|
||||
</Box>
|
||||
<Box gridColumn="span 4" gridRow="span 3" backgroundColor={theme.palette.background.alt} p="1.5rem" borderRadius="0.55rem">
|
||||
<Typography variant="h6" sx={{ color: theme.palette.secondary[100] }}> Sales By Category</Typography>
|
||||
<BreakdownChart isDashboard={true} />
|
||||
<Typography p="0 0.6rem" fontSize="0.8rem" sx={{ color: theme.palette.secondary[200] }}>
|
||||
Breakdown of real states and information via category for revenue made for this year and total sales.
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
157
admin_dashboard/client/src/pages/monthlyStat/Monthly.jsx
Normal file
157
admin_dashboard/client/src/pages/monthlyStat/Monthly.jsx
Normal file
|
@ -0,0 +1,157 @@
|
|||
import { useTheme } from '@emotion/react'
|
||||
import { Box } from '@mui/material'
|
||||
import { ResponsiveLine } from '@nivo/line'
|
||||
import Header from 'components/Header'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useGetSalesQuery } from 'state/api'
|
||||
|
||||
const Monthly = () => {
|
||||
const theme = useTheme();
|
||||
const {data} = useGetSalesQuery();
|
||||
const [formattedData] = useMemo(() => {
|
||||
if (!data) return [];
|
||||
|
||||
const {monthlyData} = data;
|
||||
const totalSalesLine = {
|
||||
id: "totalSales",
|
||||
color: theme.palette.secondary.main,
|
||||
data: [],
|
||||
};
|
||||
const totalUnitsLine = {
|
||||
id: "totalUnits",
|
||||
color: theme.palette.secondary[600],
|
||||
data: [],
|
||||
};
|
||||
Object.values(monthlyData).forEach(({ month, totalSales, totalUnits }) => {
|
||||
totalSalesLine.data = [
|
||||
...totalSalesLine.data,
|
||||
{ x: month, y: totalSales },
|
||||
];
|
||||
totalUnitsLine.data = [
|
||||
...totalUnitsLine.data,
|
||||
{ x: month, y: totalUnits },
|
||||
];
|
||||
});
|
||||
|
||||
const formattedData = [totalSalesLine, totalUnitsLine];
|
||||
return [formattedData];
|
||||
}, [data]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Box m="1.5rem 2.5rem">
|
||||
<Header title="Monthly sales" subtitle="Chart of monthly sales"/>
|
||||
<Box height="75vh">
|
||||
{data ? (
|
||||
<ResponsiveLine
|
||||
data={formattedData}
|
||||
theme={{
|
||||
axis: {
|
||||
domain: {
|
||||
line: {
|
||||
stroke: theme.palette.secondary[200],
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
text: {
|
||||
fill: theme.palette.secondary[200],
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
line: {
|
||||
stroke: theme.palette.secondary[200],
|
||||
strokeWidth: 1,
|
||||
},
|
||||
text: {
|
||||
fill: theme.palette.secondary[200],
|
||||
},
|
||||
},
|
||||
},
|
||||
legends: {
|
||||
text: {
|
||||
fill: theme.palette.secondary[200],
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
container: {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
},
|
||||
}}
|
||||
colors={{ datum: "color" }}
|
||||
margin={{ top: 50, right: 50, bottom: 70, left: 60 }}
|
||||
xScale={{ type: "point" }}
|
||||
yScale={{
|
||||
type: "linear",
|
||||
min: "auto",
|
||||
max: "auto",
|
||||
stacked: false,
|
||||
reverse: false,
|
||||
}}
|
||||
yFormat=" >-.2f"
|
||||
curve="catmullRom"
|
||||
axisTop={null}
|
||||
axisRight={null}
|
||||
axisBottom={{
|
||||
orient: "bottom",
|
||||
tickSize: 5,
|
||||
tickPadding: 5,
|
||||
tickRotation: 90,
|
||||
legend: "Month",
|
||||
legendOffset: 60,
|
||||
legendPosition: "middle",
|
||||
}}
|
||||
axisLeft={{
|
||||
orient: "left",
|
||||
tickSize: 5,
|
||||
tickPadding: 5,
|
||||
tickRotation: 0,
|
||||
legend: "Total",
|
||||
legendOffset: -50,
|
||||
legendPosition: "middle",
|
||||
}}
|
||||
enableGridX={false}
|
||||
enableGridY={false}
|
||||
pointSize={10}
|
||||
pointColor={{ theme: "background" }}
|
||||
pointBorderWidth={2}
|
||||
pointBorderColor={{ from: "serieColor" }}
|
||||
pointLabelYOffset={-12}
|
||||
useMesh={true}
|
||||
legends={[
|
||||
{
|
||||
anchor: "top-right",
|
||||
direction: "column",
|
||||
justify: false,
|
||||
translateX: 50,
|
||||
translateY: 0,
|
||||
itemsSpacing: 0,
|
||||
itemDirection: "left-to-right",
|
||||
itemWidth: 80,
|
||||
itemHeight: 20,
|
||||
itemOpacity: 0.75,
|
||||
symbolSize: 12,
|
||||
symbolShape: "circle",
|
||||
symbolBorderColor: "rgba(0, 0, 0, .5)",
|
||||
effects: [
|
||||
{
|
||||
on: "hover",
|
||||
style: {
|
||||
itemBackground: "rgba(0, 0, 0, .03)",
|
||||
itemOpacity: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : (
|
||||
<>Loading...</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Monthly
|
|
@ -3,7 +3,7 @@ import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"
|
|||
export const api = createApi({
|
||||
baseQuery: fetchBaseQuery({baseUrl: process.env.REACT_APP_BASE_URL}),
|
||||
reducerPath: "adminApi",
|
||||
tagTypes: ["User", "Products", "Customers", "Transactions", "Geography", "Sales" ],
|
||||
tagTypes: ["User", "Products", "Customers", "Transactions", "Geography", "Sales", "Dashboard" ],
|
||||
endpoints: (build) => ({
|
||||
getUser: build.query({
|
||||
query: (id) => `general/user/${id}`,
|
||||
|
@ -32,8 +32,12 @@ export const api = createApi({
|
|||
getSales: build.query({
|
||||
query: () => "sales/sales",
|
||||
providesTags: ["Sales"]
|
||||
}),
|
||||
getDashboard: build.query({
|
||||
query: () => "general/dashboard",
|
||||
providesTags: ["Dashboard"]
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
export const {useGetUserQuery, useGetProductsQuery, useGetCustomersQuery, useGetTransactionsQuery, useGetGeographyQuery, useGetSalesQuery} = api;
|
||||
export const {useGetUserQuery, useGetProductsQuery, useGetCustomersQuery, useGetTransactionsQuery, useGetGeographyQuery, useGetSalesQuery, useGetDashboardQuery} = api;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import User from "../models/User.js";
|
||||
import Stat from "../models/Stat.js"
|
||||
import Transaction from "../models/Transaction.js"
|
||||
|
||||
export const getUser = async(req, res) => {
|
||||
try {
|
||||
|
@ -7,6 +9,30 @@ export const getUser = async(req, res) => {
|
|||
res.status(200).json(user);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
res.status(404).json({message: error.message})
|
||||
}
|
||||
};
|
||||
|
||||
export const getDashboardStats = async(req, res) => {
|
||||
try {
|
||||
const currerntMonth = "November";
|
||||
const currentYear = 2021;
|
||||
const currenrtDay = "2021-04-15";
|
||||
const transactions = await Transaction.find().limit(50).sort({createdOn: -1});
|
||||
|
||||
const stat = await Stat.find({year: currentYear});
|
||||
const {totalCustomers, yearlyTotalSoldUnits, yearlySalesTotal, monthlyData, salesByCategory} = stat[0]
|
||||
|
||||
const thisMonthStats = stat[0].monthlyData.find(({month}) => {
|
||||
return month === currerntMonth;
|
||||
});
|
||||
const todayStats = stat[0].dailyData.find(({date}) => {
|
||||
return date === currenrtDay;
|
||||
});
|
||||
|
||||
res.status(200).json({totalCustomers, yearlyTotalSoldUnits, yearlySalesTotal, monthlyData, salesByCategory, thisMonthStats, todayStats, transactions})
|
||||
|
||||
} catch (error) {
|
||||
res.status(404).json({message: error.message})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import mongoose from "mongoose"
|
||||
import User from "../models/User.js"
|
||||
|
||||
export const getAdmins = async(req, res) => {
|
||||
try {
|
||||
const admins = await User.find({ role: "admin"}).select("-password");
|
||||
res.status(200).json(admins);
|
||||
|
||||
} catch (error) {
|
||||
res.status(404).json({message: error.message})
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ import mongoose from "mongoose"
|
|||
const StatSchema = new mongoose.Schema(
|
||||
{
|
||||
totalCustomers: Number,
|
||||
yaerlySalesTotal: Number,
|
||||
yearlySalesTotal: Number,
|
||||
yearlyTotalSoldUnits: Number,
|
||||
yaer: Number,
|
||||
monthlyData: [
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import express from "express"
|
||||
import {getUser} from "../controllers/general.js"
|
||||
import {getUser, getDashboardStats} from "../controllers/general.js"
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/user/:id", getUser);
|
||||
router.get("/dashboard", getDashboardStats)
|
||||
|
||||
|
||||
export default router;
|
|
@ -1,5 +1,9 @@
|
|||
import express from "express"
|
||||
import {getAdmins} from "../controllers/management.js"
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
router.get("/admin", getAdmins);
|
||||
|
||||
|
||||
export default router;
|
Loading…
Add table
Reference in a new issue