added overview & daily data
This commit is contained in:
parent
39b2da4a00
commit
3f2495f210
12 changed files with 505 additions and 28 deletions
55
admin_dashboard/client/package-lock.json
generated
55
admin_dashboard/client/package-lock.json
generated
|
@ -16,6 +16,7 @@
|
|||
"@nivo/bar": "^0.85.1",
|
||||
"@nivo/core": "^0.85.1",
|
||||
"@nivo/geo": "^0.85.1",
|
||||
"@nivo/line": "^0.85.1",
|
||||
"@nivo/pie": "^0.85.1",
|
||||
"@reduxjs/toolkit": "^2.2.3",
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
|
@ -3900,6 +3901,27 @@
|
|||
"react": ">= 16.14.0 < 19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nivo/line": {
|
||||
"version": "0.85.1",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/line/-/line-0.85.1.tgz",
|
||||
"integrity": "sha512-BLswEMiBiFxpHaRoiKp7d3S4P3gzj0OYVBojFEEG+g19lmIEeTTc7aZsXz2pTz/NdzM6fwZqTD3llIhl6LfXFg==",
|
||||
"dependencies": {
|
||||
"@nivo/annotations": "0.85.1",
|
||||
"@nivo/axes": "0.85.1",
|
||||
"@nivo/colors": "0.85.1",
|
||||
"@nivo/core": "0.85.1",
|
||||
"@nivo/legends": "0.85.1",
|
||||
"@nivo/scales": "0.85.1",
|
||||
"@nivo/tooltip": "0.85.1",
|
||||
"@nivo/voronoi": "0.85.1",
|
||||
"@react-spring/web": "9.4.5 || ^9.7.2",
|
||||
"d3-shape": "^1.3.5",
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.14.0 < 19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nivo/pie": {
|
||||
"version": "0.85.1",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/pie/-/pie-0.85.1.tgz",
|
||||
|
@ -3962,6 +3984,21 @@
|
|||
"react": ">= 16.14.0 < 19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nivo/voronoi": {
|
||||
"version": "0.85.1",
|
||||
"resolved": "https://registry.npmjs.org/@nivo/voronoi/-/voronoi-0.85.1.tgz",
|
||||
"integrity": "sha512-HJuc1Lhc7RhJyZCnn2eB1nqX6tsczUY4Z1YY3rl1Gy5HfW1vpoJZHQtWzelnvVcpj3qTrwI9QGLmDYE12HAeOQ==",
|
||||
"dependencies": {
|
||||
"@nivo/core": "0.85.1",
|
||||
"@types/d3-delaunay": "^5.3.0",
|
||||
"@types/d3-scale": "^4.0.8",
|
||||
"d3-delaunay": "^5.3.0",
|
||||
"d3-scale": "^4.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.14.0 < 19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
@ -4883,6 +4920,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
|
||||
},
|
||||
"node_modules/@types/d3-delaunay": {
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-5.3.4.tgz",
|
||||
"integrity": "sha512-GEQuDXVKQvHulQ+ecKyCubOmVjXrifAj7VR26rWVAER/IbWemaT/Tmo84ESiTtoDghg5ILdMZH7pYXQEt/Vu9A=="
|
||||
},
|
||||
"node_modules/@types/d3-format": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.4.5.tgz",
|
||||
|
@ -7714,6 +7756,14 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-delaunay": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-5.3.0.tgz",
|
||||
"integrity": "sha512-amALSrOllWVLaHTnDLHwMIiz0d1bBu9gZXd1FiLfXf8sHcX9jrcj81TVZOqD4UX7MgBZZ07c8GxzEgBpJqc74w==",
|
||||
"dependencies": {
|
||||
"delaunator": "4"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz",
|
||||
|
@ -8015,6 +8065,11 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/delaunator": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz",
|
||||
"integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag=="
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"@nivo/bar": "^0.85.1",
|
||||
"@nivo/core": "^0.85.1",
|
||||
"@nivo/geo": "^0.85.1",
|
||||
"@nivo/line": "^0.85.1",
|
||||
"@nivo/pie": "^0.85.1",
|
||||
"@reduxjs/toolkit": "^2.2.3",
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
|
|
|
@ -10,6 +10,8 @@ import Products from "pages/products/Products";
|
|||
import Customers from "pages/customers/Customers";
|
||||
import Transactions from "pages/transactions/Transactions";
|
||||
import Geography from "pages/geography/Geography";
|
||||
import Overview from "pages/overview/Overview";
|
||||
import Daily from "pages/dailyStat/Daily";
|
||||
|
||||
|
||||
function App() {
|
||||
|
@ -31,6 +33,8 @@ function App() {
|
|||
<Route path="/customers" element={<Customers/>} />
|
||||
<Route path="/transactions" element={<Transactions/>} />
|
||||
<Route path="/geography" element={<Geography/>} />
|
||||
<Route path="/overview" element={<Overview/>} />
|
||||
<Route path="/daily" element={<Daily/>}/>
|
||||
</Route>
|
||||
</Routes>
|
||||
</ThemeProvider>
|
||||
|
|
157
admin_dashboard/client/src/components/OverviewChart.jsx
Normal file
157
admin_dashboard/client/src/components/OverviewChart.jsx
Normal file
|
@ -0,0 +1,157 @@
|
|||
import React, { useMemo } from 'react'
|
||||
import {ResponsiveLine} from "@nivo/line"
|
||||
import { useTheme } from '@emotion/react'
|
||||
import { useGetSalesQuery } from 'state/api';
|
||||
|
||||
|
||||
const OverviewChart = ({ isDashboard = false, view }) => {
|
||||
const theme = useTheme();
|
||||
const {data, isLoading} = useGetSalesQuery();
|
||||
const [totalSalesLine, totalUnitsLine] = 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).reduce(
|
||||
(acc, {month, totalSales, totalUnits}) => {
|
||||
const curSales = acc.sales + totalSales;
|
||||
const curUnits = acc.units + totalUnits;
|
||||
|
||||
totalSalesLine.data = [...totalSalesLine.data, { x: month, y: curSales },];
|
||||
totalUnitsLine.data = [...totalUnitsLine.data, { x: month, y: curUnits },];
|
||||
|
||||
return {sales: curSales, units: curUnits};
|
||||
},
|
||||
{sales: 0, units: 0}
|
||||
);
|
||||
return [[totalSalesLine], [totalUnitsLine]];
|
||||
}, [data]);
|
||||
|
||||
if (!data || isLoading) return "Loading...";
|
||||
|
||||
return (
|
||||
<ResponsiveLine data={view === "sales" ? totalSalesLine : totalUnitsLine}
|
||||
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,
|
||||
},
|
||||
},
|
||||
}}
|
||||
margin={{ top: 20, right: 50, bottom: 50, left: 70 }}
|
||||
xScale={{ type: "point" }}
|
||||
yScale={{
|
||||
type: "linear",
|
||||
min: "auto",
|
||||
max: "auto",
|
||||
stacked: false,
|
||||
reverse: false,
|
||||
}}
|
||||
yFormat=" >-.2f"
|
||||
curve="catmullRom"
|
||||
enableArea={isDashboard}
|
||||
axisTop={null}
|
||||
axisRight={null}
|
||||
axisBottom={{
|
||||
format: (v) => {
|
||||
if (isDashboard) return v.slice(0, 3);
|
||||
return v;
|
||||
},
|
||||
orient: "bottom",
|
||||
tickSize: 5,
|
||||
tickPadding: 5,
|
||||
tickRotation: 0,
|
||||
legend: isDashboard ? "" : "Month",
|
||||
legendOffset: 36,
|
||||
legendPosition: "middle",
|
||||
}}
|
||||
axisLeft={{
|
||||
orient: "left",
|
||||
tickValues: 5,
|
||||
tickSize: 5,
|
||||
tickPadding: 5,
|
||||
tickRotation: 0,
|
||||
legend: isDashboard
|
||||
? ""
|
||||
: `Total ${view === "sales" ? "Revenue" : "Units"} for Year`,
|
||||
legendOffset: -60,
|
||||
legendPosition: "middle",
|
||||
}}
|
||||
enableGridX={true}
|
||||
enableGridY={true}
|
||||
pointSize={10}
|
||||
pointColor={{ theme: "background" }}
|
||||
pointBorderWidth={2}
|
||||
pointBorderColor={{ from: "serieColor" }}
|
||||
pointLabelYOffset={-12}
|
||||
useMesh={true}
|
||||
legends={
|
||||
!isDashboard
|
||||
? [
|
||||
{
|
||||
anchor: "bottom-right",
|
||||
direction: "column",
|
||||
justify: false,
|
||||
translateX: 30,
|
||||
translateY: -40,
|
||||
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,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default OverviewChart
|
176
admin_dashboard/client/src/pages/dailyStat/Daily.jsx
Normal file
176
admin_dashboard/client/src/pages/dailyStat/Daily.jsx
Normal file
|
@ -0,0 +1,176 @@
|
|||
import { Box, useTheme } from '@mui/material'
|
||||
import Header from 'components/Header'
|
||||
import React, { useState, useMemo } from 'react'
|
||||
import { useGetSalesQuery } from 'state/api';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import { ResponsiveLine } from '@nivo/line';
|
||||
|
||||
|
||||
|
||||
const Daily = () => {
|
||||
const [startDate, setStartDate] = useState(new Date("2021-02-01"));
|
||||
const [endDate, setEndDate] = useState(new Date("2021-03-01"));
|
||||
const { data } = useGetSalesQuery();
|
||||
const theme = useTheme();
|
||||
|
||||
const [formattedData] = useMemo(() => {
|
||||
if (!data) return [];
|
||||
|
||||
const { dailyData } = data;
|
||||
const totalSalesLine = {
|
||||
id: "totalSales",
|
||||
color: theme.palette.secondary.main,
|
||||
data: [],
|
||||
};
|
||||
const totalUnitsLine = {
|
||||
id: "totalUnits",
|
||||
color: theme.palette.secondary[600],
|
||||
data: [],
|
||||
};
|
||||
|
||||
Object.values(dailyData).forEach(({ date, totalSales, totalUnits }) => {
|
||||
const dateFormatted = new Date(date);
|
||||
if (dateFormatted >= startDate && dateFormatted <= endDate) {
|
||||
const splitDate = date.substring(date.indexOf("-") + 1);
|
||||
|
||||
totalSalesLine.data = [
|
||||
...totalSalesLine.data,
|
||||
{ x: splitDate, y: totalSales },
|
||||
];
|
||||
totalUnitsLine.data = [
|
||||
...totalUnitsLine.data,
|
||||
{ x: splitDate, y: totalUnits },
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
const formattedData = [totalSalesLine, totalUnitsLine];
|
||||
return [formattedData];
|
||||
}, [data, startDate, endDate]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Box m="1.5rem 2.5rem">
|
||||
<Header title="Daily sales" subtitle="Chart of daily sales"/>
|
||||
<Box height="75vh">
|
||||
<Box display="flex" justifyContent="flex-end">
|
||||
<Box>
|
||||
<DatePicker selected={startDate} onChange={(date) => setStartDate(date)} selectsStart startDate={startDate} endDate={endDate} />
|
||||
</Box>
|
||||
<Box>
|
||||
<DatePicker selected={endDate} onChange={(date) => setEndDate(date)} selectsEnd startDate={startDate} endDate={endDate} minDate={startDate} />
|
||||
</Box>
|
||||
</Box>
|
||||
{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 Daily
|
|
@ -17,38 +17,38 @@ const Geography = () => {
|
|||
{data? (
|
||||
<ResponsiveChoropleth data={data}
|
||||
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],
|
||||
},
|
||||
axis: {
|
||||
domain: {
|
||||
line: {
|
||||
stroke: theme.palette.secondary[200],
|
||||
},
|
||||
},
|
||||
legends: {
|
||||
legend: {
|
||||
text: {
|
||||
fill: theme.palette.secondary[200],
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
container: {
|
||||
color: theme.palette.primary.main,
|
||||
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,
|
||||
},
|
||||
},
|
||||
}}
|
||||
features={geographyData.features}
|
||||
margin={{ top: 0, right: 0, bottom: 0, left: -50 }}
|
||||
colors="YlGnBu"
|
||||
|
|
27
admin_dashboard/client/src/pages/overview/Overview.jsx
Normal file
27
admin_dashboard/client/src/pages/overview/Overview.jsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { Box, FormControl, InputLabel, MenuItem, Select } from '@mui/material'
|
||||
import Header from 'components/Header'
|
||||
import OverviewChart from 'components/OverviewChart';
|
||||
import React, { useState } from 'react'
|
||||
|
||||
const Overview = () => {
|
||||
const [view, setView] = useState("units");
|
||||
|
||||
|
||||
return (
|
||||
<Box m="1.5rem 2.5rem">
|
||||
<Header title="Overview" subtitle="Overview of general revenue and profit"/>
|
||||
<Box height="75vh" mt="2rem">
|
||||
<FormControl>
|
||||
<InputLabel>View</InputLabel>
|
||||
<Select label="View" value={view} onChange={(e) => setView(e.target.value)}>
|
||||
<MenuItem value="sales">Sales</MenuItem>
|
||||
<MenuItem value="units">Unit</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<OverviewChart view={view}/>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Overview
|
|
@ -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" ],
|
||||
tagTypes: ["User", "Products", "Customers", "Transactions", "Geography", "Sales" ],
|
||||
endpoints: (build) => ({
|
||||
getUser: build.query({
|
||||
query: (id) => `general/user/${id}`,
|
||||
|
@ -28,8 +28,12 @@ export const api = createApi({
|
|||
getGeography: build.query({
|
||||
query: () => "client/geography",
|
||||
providesTags: ["Geography"],
|
||||
}),
|
||||
getSales: build.query({
|
||||
query: () => "sales/sales",
|
||||
providesTags: ["Sales"]
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
export const {useGetUserQuery, useGetProductsQuery, useGetCustomersQuery, useGetTransactionsQuery, useGetGeographyQuery} = api;
|
||||
export const {useGetUserQuery, useGetProductsQuery, useGetCustomersQuery, useGetTransactionsQuery, useGetGeographyQuery, useGetSalesQuery} = api;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import Stat from "../models/Stat.js"
|
||||
|
||||
|
||||
|
||||
|
||||
export const getSales =async(req, res) => {
|
||||
try {
|
||||
const stats = await Stat.find();
|
||||
res.status(200).json(stats[0]);
|
||||
|
||||
} catch (error) {
|
||||
res.status(404).json({message: error.message})
|
||||
}
|
||||
}
|
|
@ -10,11 +10,11 @@ import generalRoutes from "./routes/general.js"
|
|||
import managmentRoutes from "./routes/management.js"
|
||||
import saleRoutes from "./routes/sales.js"
|
||||
import User from "./models/User.js"
|
||||
import {dataUser, dataProduct, dataProductStat, dataTransaction} from "./data/index.js"
|
||||
import {dataUser, dataProduct, dataProductStat, dataTransaction, dataOverallStat} from "./data/index.js"
|
||||
import Product from "./models/Product.js"
|
||||
import ProductStat from "./models/ProductStat.js"
|
||||
import Transaction from "./models/Transaction.js"
|
||||
|
||||
import Stat from "./models/Stat.js"
|
||||
|
||||
//configuration setup
|
||||
dotenv.config();
|
||||
|
@ -48,6 +48,8 @@ mongoose.connect(process.env.MONGO_URL, {
|
|||
//ProductStat.insertMany(dataProductStat);
|
||||
//User.insertMany(dataUser);
|
||||
//Transaction.insertMany(dataTransaction);
|
||||
//Stat.insertMany(dataOverallStat);
|
||||
|
||||
}).catch((error) => console.log(`${error} didn't connect`))
|
||||
|
||||
|
||||
|
|
32
admin_dashboard/server/models/Stat.js
Normal file
32
admin_dashboard/server/models/Stat.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import mongoose from "mongoose"
|
||||
|
||||
const StatSchema = new mongoose.Schema(
|
||||
{
|
||||
totalCustomers: Number,
|
||||
yaerlySalesTotal: Number,
|
||||
yearlyTotalSoldUnits: Number,
|
||||
yaer: Number,
|
||||
monthlyData: [
|
||||
{
|
||||
month: String,
|
||||
totalSales: Number,
|
||||
totalUnits: Number,
|
||||
},
|
||||
],
|
||||
dailyData: [
|
||||
{
|
||||
date: String,
|
||||
totalSales: Number,
|
||||
totalUnits: Number,
|
||||
},
|
||||
],
|
||||
salesByCategory:{
|
||||
type: Map,
|
||||
of: Number,
|
||||
},
|
||||
},
|
||||
{timestamps: true}
|
||||
);
|
||||
|
||||
const Stat = mongoose.model("Stat", StatSchema);
|
||||
export default Stat;
|
|
@ -1,5 +1,10 @@
|
|||
import express from "express"
|
||||
import {getSales} from "../controllers/sales.js"
|
||||
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/sales", getSales)
|
||||
|
||||
export default router;
|
Loading…
Add table
Reference in a new issue