├── public
├── robots.txt
├── favicon.ico
├── logo192.png
├── logo512.png
├── manifest.json
└── index.html
├── src
├── Components
│ ├── PageContent
│ │ └── index.js
│ ├── AppFooter
│ │ └── index.js
│ ├── AppRoutes
│ │ └── index.js
│ ├── SideMenu
│ │ └── index.js
│ └── AppHeader
│ │ └── index.js
├── setupTests.js
├── App.test.js
├── index.css
├── reportWebVitals.js
├── App.js
├── API
│ └── index.js
├── index.js
├── App.css
└── Pages
│ ├── Orders
│ └── index.js
│ ├── Customers
│ └── index.js
│ ├── Inventory
│ └── index.js
│ └── Dashbaord
│ └── index.js
├── README.md
├── LICENSE
└── package.json
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aamirjaved844/Dashbaord-Tutorial-using-React-and-Ant-Design/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aamirjaved844/Dashbaord-Tutorial-using-React-and-Ant-Design/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aamirjaved844/Dashbaord-Tutorial-using-React-and-Ant-Design/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/src/Components/PageContent/index.js:
--------------------------------------------------------------------------------
1 | import AppRoutes from "../AppRoutes";
2 |
3 | function PageContent() {
4 | return (
5 |
8 | );
9 | }
10 | export default PageContent;
11 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/Components/AppFooter/index.js:
--------------------------------------------------------------------------------
1 | import { Typography } from "antd";
2 |
3 | function AppFooter() {
4 | return (
5 |
6 | +123456789
7 |
8 | Privacy Policy
9 |
10 |
11 | Terms of Use
12 |
13 |
14 | );
15 | }
16 | export default AppFooter;
17 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { Space } from "antd";
2 | import "./App.css";
3 | import AppFooter from "./Components/AppFooter";
4 | import AppHeader from "./Components/AppHeader";
5 | import PageContent from "./Components/PageContent";
6 | import SideMenu from "./Components/SideMenu";
7 |
8 | function App() {
9 | return (
10 |
18 | );
19 | }
20 | export default App;
21 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/src/API/index.js:
--------------------------------------------------------------------------------
1 | export const getOrders = () => {
2 | return fetch("https://dummyjson.com/carts/1").then((res) => res.json());
3 | };
4 |
5 | export const getRevenue = () => {
6 | return fetch("https://dummyjson.com/carts").then((res) => res.json());
7 | };
8 |
9 | export const getInventory = () => {
10 | return fetch("https://dummyjson.com/products").then((res) => res.json());
11 | };
12 |
13 | export const getCustomers = () => {
14 | return fetch("https://dummyjson.com/users").then((res) => res.json());
15 | };
16 | export const getComments = () => {
17 | return fetch("https://dummyjson.com/comments").then((res) => res.json());
18 | };
19 |
--------------------------------------------------------------------------------
/src/Components/AppRoutes/index.js:
--------------------------------------------------------------------------------
1 | import { BrowserRouter, Route, Routes } from "react-router-dom";
2 | import Customers from "../../Pages/Customers";
3 | import Dashboard from "../../Pages/Dashbaord";
4 | import Inventory from "../../Pages/Inventory";
5 | import Orders from "../../Pages/Orders";
6 |
7 | function AppRoutes() {
8 | return (
9 |
10 | }>
11 | }>
12 | }>
13 | }>
14 |
15 | );
16 | }
17 | export default AppRoutes;
18 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 | import reportWebVitals from "./reportWebVitals";
6 | import { BrowserRouter } from "react-router-dom";
7 |
8 | const root = ReactDOM.createRoot(document.getElementById("root"));
9 | root.render(
10 |
11 |
12 |
13 |
14 |
15 | );
16 |
17 | // If you want to start measuring performance in your app, pass a function
18 | // to log results (for example: reportWebVitals(console.log))
19 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
20 | reportWebVitals();
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dashbaord-Tutorial-using-React-and-Ant-Design
2 | This repo contains code base regarding youtube tutorial (https://youtu.be/xvBUgdKUz5g) on how to develop a simple admin Dashboard using ReactJS and Ant Design UI Components.
3 |
4 | The main features includes:
5 | - App Header with logo, name and notificaiton icons
6 | - App Footer with static links
7 | - App Sidebar for pages routing using react-router-dom
8 | - API integration to fetch data from server
9 | - Dashboard page to show statistics cards, recenet orders, and revenue chart using ChartJS
10 | - Different Table pages like Inventory, Orders and Customers list
11 | - Notificationa and Comments Drawer opened from Header
12 | - Other misc. functions used in admin dashboard portal
13 |
14 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App{
2 | display: flex;
3 | flex-direction: column;
4 | width: 100vw;
5 | height: 100vh;
6 | }
7 | .AppHeader{
8 | height: 50px;
9 | display: flex;
10 | justify-content: space-between;
11 | align-items: center;
12 | padding: 4px 20px 4px 12px;
13 | border-bottom: 1px solid rgba(0, 0, 0, 0.15);
14 | }
15 | .AppFooter{
16 | height: 50px;
17 | display: flex;
18 | justify-content: space-evenly;
19 | align-items: center;
20 | border-top: 1px solid rgba(0, 0, 0, 0.15);
21 |
22 | }
23 | .SideMenuAndPageContent{
24 | display: flex;
25 | flex: 1;
26 | justify-content: flex-start;
27 | align-items: flex-start;
28 | background-color: rgba(0, 0, 0, 0.05) ;
29 | }
30 | .SideMenu{
31 | height: 100%;
32 | }
33 | .SideMenuVertical{
34 | height: 100%;
35 | }
36 | .PageContent{
37 | padding-left: 12px;
38 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Aamir J
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "antd-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@ant-design/icons": "^4.8.0",
7 | "@testing-library/jest-dom": "^5.16.5",
8 | "@testing-library/react": "^13.4.0",
9 | "@testing-library/user-event": "^13.5.0",
10 | "antd": "^5.1.2",
11 | "chart.js": "^4.1.2",
12 | "react": "^18.2.0",
13 | "react-chartjs-2": "^5.2.0",
14 | "react-dom": "^18.2.0",
15 | "react-router-dom": "^6.4.3",
16 | "react-scripts": "5.0.1",
17 | "web-vitals": "^2.1.4"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Pages/Orders/index.js:
--------------------------------------------------------------------------------
1 | import { Avatar, Rate, Space, Table, Typography } from "antd";
2 | import { useEffect, useState } from "react";
3 | import { getInventory, getOrders } from "../../API";
4 |
5 | function Orders() {
6 | const [loading, setLoading] = useState(false);
7 | const [dataSource, setDataSource] = useState([]);
8 |
9 | useEffect(() => {
10 | setLoading(true);
11 | getOrders().then((res) => {
12 | setDataSource(res.products);
13 | setLoading(false);
14 | });
15 | }, []);
16 |
17 | return (
18 |
19 | Orders
20 | ${value},
31 | },
32 | {
33 | title: "DiscountedPrice",
34 | dataIndex: "discountedPrice",
35 | render: (value) => ${value},
36 | },
37 | {
38 | title: "Quantity",
39 | dataIndex: "quantity",
40 | },
41 | {
42 | title: "Total",
43 | dataIndex: "total",
44 | },
45 | ]}
46 | dataSource={dataSource}
47 | pagination={{
48 | pageSize: 5,
49 | }}
50 | >
51 |
52 | );
53 | }
54 | export default Orders;
55 |
--------------------------------------------------------------------------------
/src/Components/SideMenu/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | AppstoreOutlined,
3 | ShopOutlined,
4 | ShoppingCartOutlined,
5 | UserOutlined,
6 | } from "@ant-design/icons";
7 | import { Menu } from "antd";
8 | import { useEffect, useState } from "react";
9 | import { useLocation, useNavigate } from "react-router-dom";
10 |
11 | function SideMenu() {
12 | const location = useLocation();
13 | const [selectedKeys, setSelectedKeys] = useState("/");
14 |
15 | useEffect(() => {
16 | const pathName = location.pathname;
17 | setSelectedKeys(pathName);
18 | }, [location.pathname]);
19 |
20 | const navigate = useNavigate();
21 | return (
22 |
23 |
54 |
55 | );
56 | }
57 | export default SideMenu;
58 |
--------------------------------------------------------------------------------
/src/Pages/Customers/index.js:
--------------------------------------------------------------------------------
1 | import { Avatar, Rate, Space, Table, Typography } from "antd";
2 | import { useEffect, useState } from "react";
3 | import { getCustomers, getInventory } from "../../API";
4 |
5 | function Customers() {
6 | const [loading, setLoading] = useState(false);
7 | const [dataSource, setDataSource] = useState([]);
8 |
9 | useEffect(() => {
10 | setLoading(true);
11 | getCustomers().then((res) => {
12 | setDataSource(res.users);
13 | setLoading(false);
14 | });
15 | }, []);
16 |
17 | return (
18 |
19 | Customers
20 | {
27 | return ;
28 | },
29 | },
30 | {
31 | title: "First Name",
32 | dataIndex: "firstName",
33 | },
34 | {
35 | title: "LastName",
36 | dataIndex: "lastName",
37 | },
38 | {
39 | title: "Email",
40 | dataIndex: "email",
41 | },
42 | {
43 | title: "Phone",
44 | dataIndex: "phone",
45 | },
46 |
47 | {
48 | title: "address",
49 | dataIndex: "address",
50 | render: (address) => {
51 | return (
52 |
53 | {address.address}, {address.city}
54 |
55 | );
56 | },
57 | },
58 | ]}
59 | dataSource={dataSource}
60 | pagination={{
61 | pageSize: 5,
62 | }}
63 | >
64 |
65 | );
66 | }
67 | export default Customers;
68 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/Pages/Inventory/index.js:
--------------------------------------------------------------------------------
1 | import { Avatar, Rate, Space, Table, Typography } from "antd";
2 | import { useEffect, useState } from "react";
3 | import { getInventory } from "../../API";
4 |
5 | function Inventory() {
6 | const [loading, setLoading] = useState(false);
7 | const [dataSource, setDataSource] = useState([]);
8 |
9 | useEffect(() => {
10 | setLoading(true);
11 | getInventory().then((res) => {
12 | setDataSource(res.products);
13 | setLoading(false);
14 | });
15 | }, []);
16 |
17 | return (
18 |
19 | Inventory
20 | {
27 | return ;
28 | },
29 | },
30 | {
31 | title: "Title",
32 | dataIndex: "title",
33 | },
34 | {
35 | title: "Price",
36 | dataIndex: "price",
37 | render: (value) => ${value},
38 | },
39 | {
40 | title: "Rating",
41 | dataIndex: "rating",
42 | render: (rating) => {
43 | return ;
44 | },
45 | },
46 | {
47 | title: "Stock",
48 | dataIndex: "stock",
49 | },
50 |
51 | {
52 | title: "Brand",
53 | dataIndex: "brand",
54 | },
55 | {
56 | title: "Category",
57 | dataIndex: "category",
58 | },
59 | ]}
60 | dataSource={dataSource}
61 | pagination={{
62 | pageSize: 5,
63 | }}
64 | >
65 |
66 | );
67 | }
68 | export default Inventory;
69 |
--------------------------------------------------------------------------------
/src/Components/AppHeader/index.js:
--------------------------------------------------------------------------------
1 | import { BellFilled, MailOutlined } from "@ant-design/icons";
2 | import { Badge, Drawer, Image, List, Space, Typography } from "antd";
3 | import { useEffect, useState } from "react";
4 | import { getComments, getOrders } from "../../API";
5 |
6 | function AppHeader() {
7 | const [comments, setComments] = useState([]);
8 | const [orders, setOrders] = useState([]);
9 | const [commentsOpen, setCommentsOpen] = useState(false);
10 | const [notificationsOpen, setNotificationsOpen] = useState(false);
11 |
12 | useEffect(() => {
13 | getComments().then((res) => {
14 | setComments(res.comments);
15 | });
16 | getOrders().then((res) => {
17 | setOrders(res.products);
18 | });
19 | }, []);
20 |
21 | return (
22 |
23 |
27 | Aamir's Dashboard
28 |
29 |
30 | {
33 | setCommentsOpen(true);
34 | }}
35 | />
36 |
37 |
38 | {
41 | setNotificationsOpen(true);
42 | }}
43 | />
44 |
45 |
46 | {
50 | setCommentsOpen(false);
51 | }}
52 | maskClosable
53 | >
54 | {
57 | return {item.body};
58 | }}
59 | >
60 |
61 | {
65 | setNotificationsOpen(false);
66 | }}
67 | maskClosable
68 | >
69 | {
72 | return (
73 |
74 | {item.title} has been
75 | ordered!
76 |
77 | );
78 | }}
79 | >
80 |
81 |
82 | );
83 | }
84 | export default AppHeader;
85 |
--------------------------------------------------------------------------------
/src/Pages/Dashbaord/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | DollarCircleOutlined,
3 | ShoppingCartOutlined,
4 | ShoppingOutlined,
5 | UserOutlined,
6 | } from "@ant-design/icons";
7 | import { Card, Space, Statistic, Table, Typography } from "antd";
8 | import { useEffect, useState } from "react";
9 | import { getCustomers, getInventory, getOrders, getRevenue } from "../../API";
10 |
11 | import {
12 | Chart as ChartJS,
13 | CategoryScale,
14 | LinearScale,
15 | BarElement,
16 | Title,
17 | Tooltip,
18 | Legend,
19 | } from "chart.js";
20 | import { Bar } from "react-chartjs-2";
21 |
22 | ChartJS.register(
23 | CategoryScale,
24 | LinearScale,
25 | BarElement,
26 | Title,
27 | Tooltip,
28 | Legend
29 | );
30 |
31 | function Dashboard() {
32 | const [orders, setOrders] = useState(0);
33 | const [inventory, setInventory] = useState(0);
34 | const [customers, setCustomers] = useState(0);
35 | const [revenue, setRevenue] = useState(0);
36 |
37 | useEffect(() => {
38 | getOrders().then((res) => {
39 | setOrders(res.total);
40 | setRevenue(res.discountedTotal);
41 | });
42 | getInventory().then((res) => {
43 | setInventory(res.total);
44 | });
45 | getCustomers().then((res) => {
46 | setCustomers(res.total);
47 | });
48 | }, []);
49 |
50 | return (
51 |
52 | Dashboard
53 |
54 |
65 | }
66 | title={"Orders"}
67 | value={orders}
68 | />
69 |
80 | }
81 | title={"Inventory"}
82 | value={inventory}
83 | />
84 |
95 | }
96 | title={"Customer"}
97 | value={customers}
98 | />
99 |
110 | }
111 | title={"Revenue"}
112 | value={revenue}
113 | />
114 |
115 |
116 |
117 |
118 |
119 |
120 | );
121 | }
122 |
123 | function DashboardCard({ title, value, icon }) {
124 | return (
125 |
126 |
127 | {icon}
128 |
129 |
130 |
131 | );
132 | }
133 | function RecentOrders() {
134 | const [dataSource, setDataSource] = useState([]);
135 | const [loading, setLoading] = useState(false);
136 |
137 | useEffect(() => {
138 | setLoading(true);
139 | getOrders().then((res) => {
140 | setDataSource(res.products.splice(0, 3));
141 | setLoading(false);
142 | });
143 | }, []);
144 |
145 | return (
146 | <>
147 | Recent Orders
148 |
167 | >
168 | );
169 | }
170 |
171 | function DashboardChart() {
172 | const [reveneuData, setReveneuData] = useState({
173 | labels: [],
174 | datasets: [],
175 | });
176 |
177 | useEffect(() => {
178 | getRevenue().then((res) => {
179 | const labels = res.carts.map((cart) => {
180 | return `User-${cart.userId}`;
181 | });
182 | const data = res.carts.map((cart) => {
183 | return cart.discountedTotal;
184 | });
185 |
186 | const dataSource = {
187 | labels,
188 | datasets: [
189 | {
190 | label: "Revenue",
191 | data: data,
192 | backgroundColor: "rgba(255, 0, 0, 1)",
193 | },
194 | ],
195 | };
196 |
197 | setReveneuData(dataSource);
198 | });
199 | }, []);
200 |
201 | const options = {
202 | responsive: true,
203 | plugins: {
204 | legend: {
205 | position: "bottom",
206 | },
207 | title: {
208 | display: true,
209 | text: "Order Revenue",
210 | },
211 | },
212 | };
213 |
214 | return (
215 |
216 |
217 |
218 | );
219 | }
220 | export default Dashboard;
221 |
--------------------------------------------------------------------------------