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 Geography from "pages/geography/Geography";
|
||||||
import Overview from "pages/overview/Overview";
|
import Overview from "pages/overview/Overview";
|
||||||
import Daily from "pages/dailyStat/Daily";
|
import Daily from "pages/dailyStat/Daily";
|
||||||
|
import Monthly from "pages/monthlyStat/Monthly";
|
||||||
|
import Breakdown from "pages/breakdown/Breakdown";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
@ -35,6 +38,8 @@ function App() {
|
||||||
<Route path="/geography" element={<Geography/>} />
|
<Route path="/geography" element={<Geography/>} />
|
||||||
<Route path="/overview" element={<Overview/>} />
|
<Route path="/overview" element={<Overview/>} />
|
||||||
<Route path="/daily" element={<Daily/>}/>
|
<Route path="/daily" element={<Daily/>}/>
|
||||||
|
<Route path="/monthly" element={<Monthly/>}/>
|
||||||
|
<Route path="/breakdown" element={<Breakdown/>}/>
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</ThemeProvider>
|
</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 React from 'react'
|
||||||
import { Box, Divider, Drawer, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography, useTheme } from '@mui/material'
|
import { Box, Divider, Drawer, IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, Typography, useTheme } from '@mui/material'
|
||||||
import { SettingsOutlined, ChevronLeft, ChevronRightOutlined, HomeOutlined, Groups2Outlined, ReceiptLongOutlined, PublicOutlined,
|
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 CategoryOutlinedIcon from '@mui/icons-material/CategoryOutlined';
|
||||||
import PlagiarismOutlinedIcon from '@mui/icons-material/PlagiarismOutlined';
|
import PlagiarismOutlinedIcon from '@mui/icons-material/PlagiarismOutlined';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
|
@ -54,18 +54,6 @@ const navItems = [
|
||||||
{
|
{
|
||||||
text: "Breakdown",
|
text: "Breakdown",
|
||||||
icon: <PieChartOutlined />
|
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">
|
<Box m="1.5rem 2rem 2rem 3rem">
|
||||||
<FlexBetween color={theme.palette.secondary.main}>
|
<FlexBetween color={theme.palette.secondary.main}>
|
||||||
<Box display="flex" alignItems="center" gap="0.5rem">
|
<Box display="flex" alignItems="center" gap="0.5rem">
|
||||||
<Typography variant='h4' fontWeight="bold" >
|
<Typography variant='h5' fontWeight="bold">
|
||||||
Jtp Dashboard
|
Analytics Dashboard
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
{!nonMobile && (
|
{!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 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 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 (
|
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({
|
export const api = createApi({
|
||||||
baseQuery: fetchBaseQuery({baseUrl: process.env.REACT_APP_BASE_URL}),
|
baseQuery: fetchBaseQuery({baseUrl: process.env.REACT_APP_BASE_URL}),
|
||||||
reducerPath: "adminApi",
|
reducerPath: "adminApi",
|
||||||
tagTypes: ["User", "Products", "Customers", "Transactions", "Geography", "Sales" ],
|
tagTypes: ["User", "Products", "Customers", "Transactions", "Geography", "Sales", "Dashboard" ],
|
||||||
endpoints: (build) => ({
|
endpoints: (build) => ({
|
||||||
getUser: build.query({
|
getUser: build.query({
|
||||||
query: (id) => `general/user/${id}`,
|
query: (id) => `general/user/${id}`,
|
||||||
|
@ -32,8 +32,12 @@ export const api = createApi({
|
||||||
getSales: build.query({
|
getSales: build.query({
|
||||||
query: () => "sales/sales",
|
query: () => "sales/sales",
|
||||||
providesTags: ["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 User from "../models/User.js";
|
||||||
|
import Stat from "../models/Stat.js"
|
||||||
|
import Transaction from "../models/Transaction.js"
|
||||||
|
|
||||||
export const getUser = async(req, res) => {
|
export const getUser = async(req, res) => {
|
||||||
try {
|
try {
|
||||||
|
@ -7,6 +9,30 @@ export const getUser = async(req, res) => {
|
||||||
res.status(200).json(user);
|
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) {
|
} catch (error) {
|
||||||
res.status(404).json({message: error.message})
|
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(
|
const StatSchema = new mongoose.Schema(
|
||||||
{
|
{
|
||||||
totalCustomers: Number,
|
totalCustomers: Number,
|
||||||
yaerlySalesTotal: Number,
|
yearlySalesTotal: Number,
|
||||||
yearlyTotalSoldUnits: Number,
|
yearlyTotalSoldUnits: Number,
|
||||||
yaer: Number,
|
yaer: Number,
|
||||||
monthlyData: [
|
monthlyData: [
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import express from "express"
|
import express from "express"
|
||||||
import {getUser} from "../controllers/general.js"
|
import {getUser, getDashboardStats} from "../controllers/general.js"
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get("/user/:id", getUser);
|
router.get("/user/:id", getUser);
|
||||||
|
router.get("/dashboard", getDashboardStats)
|
||||||
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
|
@ -1,5 +1,9 @@
|
||||||
import express from "express"
|
import express from "express"
|
||||||
|
import {getAdmins} from "../controllers/management.js"
|
||||||
|
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
router.get("/admin", getAdmins);
|
||||||
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
Loading…
Add table
Reference in a new issue