├── src ├── vite-env.d.ts ├── assets │ ├── userpic.png │ ├── images │ │ ├── heads.png │ │ └── tails.png │ ├── data.json │ └── react.svg ├── components │ ├── Loader.tsx │ ├── DashboardTable.tsx │ ├── TableHOC.tsx │ ├── AdminSidebar.tsx │ └── Charts.tsx ├── main.tsx ├── styles │ ├── _chart.scss │ ├── _mixin.scss │ ├── _mediaquery.scss │ ├── _dashboardapp.scss │ ├── _products.scss │ ├── _dashboard.scss │ └── app.scss ├── types.ts ├── pages │ ├── apps │ │ ├── Toss.tsx │ │ ├── Stopwatch.tsx │ │ └── Coupon.tsx │ ├── charts │ │ ├── BarCharts.tsx │ │ ├── LineCharts.tsx │ │ └── PieCharts.tsx │ ├── Transaction.tsx │ ├── management │ │ ├── NewProduct.tsx │ │ ├── ProductManagement.tsx │ │ └── TransactionManagement.tsx │ ├── Customers.tsx │ ├── Products.tsx │ └── Dashboard.tsx ├── App.tsx └── react-table-config.d.ts ├── vite.config.ts ├── tsconfig.node.json ├── .gitignore ├── index.html ├── .eslintrc.cjs ├── tsconfig.json ├── package.json ├── README.md └── public └── vite.svg /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/assets/userpic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meabhisingh/react-admin-dashboard-ts/HEAD/src/assets/userpic.png -------------------------------------------------------------------------------- /src/assets/images/heads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meabhisingh/react-admin-dashboard-ts/HEAD/src/assets/images/heads.png -------------------------------------------------------------------------------- /src/assets/images/tails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meabhisingh/react-admin-dashboard-ts/HEAD/src/assets/images/tails.png -------------------------------------------------------------------------------- /src/components/Loader.tsx: -------------------------------------------------------------------------------- 1 | const Loader = () => { 2 | return ( 3 |
4 |
5 |
6 | ); 7 | }; 8 | 9 | export default Loader; 10 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | import "./styles/app.scss"; 5 | 6 | ReactDOM.createRoot(document.getElementById("root")!).render( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /src/styles/_chart.scss: -------------------------------------------------------------------------------- 1 | .chart-container { 2 | background-color: white; 3 | padding: 4rem; 4 | overflow-y: auto; 5 | > h1 { 6 | margin: 0 0 5rem 2rem; 7 | } 8 | > section { 9 | width: 80%; 10 | margin: 4rem auto; 11 | 12 | > div { 13 | max-width: 25rem; 14 | margin: auto; 15 | margin-top: 6rem; 16 | margin-bottom: -1rem; 17 | } 18 | 19 | > h2 { 20 | margin: 2rem 0; 21 | text-align: center; 22 | @include heading(2px); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type OrderItemType = { 2 | name: string; 3 | photo: string; 4 | price: number; 5 | quantity: number; 6 | _id: string; 7 | }; 8 | 9 | export type OrderType = { 10 | name: string; 11 | address: string; 12 | city: string; 13 | country: string; 14 | state: string; 15 | pinCode: number; 16 | status: "Processing" | "Shipped" | "Delivered"; 17 | subtotal: number; 18 | discount: number; 19 | shippingCharges: number; 20 | tax: number; 21 | total: number; 22 | orderItems: OrderItemType[]; 23 | _id: string; 24 | }; 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /src/components/DashboardTable.tsx: -------------------------------------------------------------------------------- 1 | import { Column } from "react-table"; 2 | import TableHOC from "./TableHOC"; 3 | 4 | interface DataType { 5 | id: string; 6 | quantity: number; 7 | discount: number; 8 | amount: number; 9 | status: string; 10 | } 11 | 12 | const columns: Column[] = [ 13 | { 14 | Header: "Id", 15 | accessor: "id", 16 | }, 17 | { 18 | Header: "Quantity", 19 | accessor: "quantity", 20 | }, 21 | { 22 | Header: "Discount", 23 | accessor: "discount", 24 | }, 25 | { 26 | Header: "Amount", 27 | accessor: "amount", 28 | }, 29 | { 30 | Header: "Status", 31 | accessor: "status", 32 | }, 33 | ]; 34 | 35 | const DashboardTable = ({ data = [] }: { data: DataType[] }) => { 36 | return TableHOC( 37 | columns, 38 | data, 39 | "transaction-box", 40 | "Top Transaction" 41 | )(); 42 | }; 43 | 44 | export default DashboardTable; 45 | -------------------------------------------------------------------------------- /src/pages/apps/Toss.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import AdminSidebar from "../../components/AdminSidebar"; 3 | 4 | const Toss = () => { 5 | const [angle, setAngle] = useState(0); 6 | 7 | const flipCoin = () => { 8 | if (Math.random() > 0.5) setAngle((prev) => prev + 180); 9 | else setAngle((prev) => prev + 360); 10 | }; 11 | 12 | return ( 13 |
14 | 15 |
16 |

Toss

17 |
18 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | ); 32 | }; 33 | 34 | export default Toss; 35 | -------------------------------------------------------------------------------- /src/styles/_mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin heading($spacing: 3px, $weight: 300, $case: uppercase) { 2 | letter-spacing: $spacing; 3 | font-weight: $weight; 4 | text-transform: $case; 5 | } 6 | 7 | @mixin flex( 8 | $dir: row, 9 | $justifyContent: center, 10 | $alignItems: center, 11 | $gap: 1rem 12 | ) { 13 | display: flex; 14 | flex-direction: $dir; 15 | justify-content: $justifyContent; 16 | align-items: $alignItems; 17 | gap: $gap; 18 | } 19 | @mixin grid($col: 1fr, $gap: 1rem, $row: unset) { 20 | display: grid; 21 | grid-template-columns: $col; 22 | grid-template-rows: $row; 23 | gap: $gap; 24 | } 25 | 26 | @mixin inputStyle($w: 100%, $p: 1rem, $bgColor: inherit, $border: none) { 27 | padding: $p; 28 | border: $border; 29 | background-color: $bgColor; 30 | width: $w; 31 | outline: none; 32 | } 33 | 34 | @mixin posCenter { 35 | position: absolute; 36 | top: 50%; 37 | left: 50%; 38 | transform: translate(-50%, -50%); 39 | } 40 | 41 | @mixin square($s: 1rem) { 42 | height: $s; 43 | width: $s; 44 | } 45 | -------------------------------------------------------------------------------- /src/assets/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "categories": [ 3 | { 4 | "value": 40, 5 | "heading": "Laptops" 6 | }, 7 | 8 | { 9 | "value": 100, 10 | "heading": "Shoes" 11 | }, 12 | { 13 | "value": 80, 14 | "heading": "Cameras" 15 | }, 16 | { 17 | "value": 60, 18 | "heading": "Jeans" 19 | } 20 | ], 21 | 22 | "transaction": [ 23 | { 24 | "id": "ksdnfkjsdfx", 25 | "amount": 4000, 26 | "quantity": 4, 27 | "discount": 300, 28 | "status": "Processing" 29 | }, 30 | { 31 | "id": "sdsdssdsd", 32 | "amount": 5100, 33 | "quantity": 2, 34 | "discount": 900, 35 | "status": "Processing" 36 | }, 37 | { 38 | "id": "sdsdcvsssdsd", 39 | "amount": 13000, 40 | "quantity": 91, 41 | "discount": 0, 42 | "status": "Shipped" 43 | }, 44 | { 45 | "id": "dfddddfd", 46 | "amount": 2300, 47 | "quantity": 4, 48 | "discount": 2000, 49 | "status": "Processing" 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin-dashboard", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "chart.js": "^4.4.0", 14 | "react": "^18.2.0", 15 | "react-chartjs-2": "^5.2.0", 16 | "react-dom": "^18.2.0", 17 | "react-icons": "^4.10.1", 18 | "react-router-dom": "^6.15.0", 19 | "react-table": "^7.8.0", 20 | "sass": "^1.66.1" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.2.15", 24 | "@types/react-dom": "^18.2.7", 25 | "@types/react-table": "^7.7.15", 26 | "@typescript-eslint/eslint-plugin": "^6.0.0", 27 | "@typescript-eslint/parser": "^6.0.0", 28 | "@vitejs/plugin-react-swc": "^3.3.2", 29 | "eslint": "^8.45.0", 30 | "eslint-plugin-react-hooks": "^4.6.0", 31 | "eslint-plugin-react-refresh": "^0.4.3", 32 | "typescript": "^5.0.2", 33 | "vite": "^4.4.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/styles/_mediaquery.scss: -------------------------------------------------------------------------------- 1 | @media screen and (width<= 1200px) { 2 | .admin-container { 3 | overflow: auto; 4 | } 5 | 6 | .dashboard { 7 | .widget-container, 8 | .graph-container, 9 | .transaction-container { 10 | justify-content: center; 11 | flex-wrap: wrap; 12 | } 13 | .graph-container { 14 | padding: 2rem; 15 | } 16 | .transaction-container { 17 | padding: 2rem; 18 | height: unset; 19 | } 20 | } 21 | 22 | .product-management { 23 | padding: 2rem; 24 | } 25 | } 26 | 27 | @media screen and (width<= 1100px) { 28 | .admin-container { 29 | grid-template-columns: 1fr !important; 30 | } 31 | } 32 | 33 | @media screen and (width<= 600px) { 34 | .product-management { 35 | flex-direction: column; 36 | align-items: center; 37 | padding: 0; 38 | > section { 39 | max-width: 400px; 40 | } 41 | } 42 | 43 | .chart-container { 44 | padding: 0; 45 | > h1 { 46 | margin: 0; 47 | text-align: center; 48 | } 49 | > section { 50 | margin: 8rem auto; 51 | } 52 | } 53 | 54 | .dashboard-app-container > section { 55 | .coupon-form, 56 | .stopwatch { 57 | display: flex; 58 | flex-direction: column; 59 | } 60 | .stopwatch { 61 | align-items: center; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | parserOptions: { 18 | ecmaVersion: 'latest', 19 | sourceType: 'module', 20 | project: ['./tsconfig.json', './tsconfig.node.json'], 21 | tsconfigRootDir: __dirname, 22 | }, 23 | ``` 24 | 25 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 26 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 27 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 28 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/charts/BarCharts.tsx: -------------------------------------------------------------------------------- 1 | import AdminSidebar from "../../components/AdminSidebar"; 2 | import { BarChart } from "../../components/Charts"; 3 | 4 | const months = [ 5 | "January", 6 | "February", 7 | "March", 8 | "April", 9 | "May", 10 | "June", 11 | "July", 12 | "Aug", 13 | "Sept", 14 | "Oct", 15 | "Nov", 16 | "Dec", 17 | ]; 18 | 19 | const BarCharts = () => { 20 | return ( 21 |
22 | 23 |
24 |

Bar Charts

25 |
26 | 34 |

Top Selling Products & Top Customers

35 |
36 |
37 | 49 |

Orders throughout the year

50 |
51 |
52 |
53 | ); 54 | }; 55 | 56 | export default BarCharts; 57 | -------------------------------------------------------------------------------- /src/pages/apps/Stopwatch.tsx: -------------------------------------------------------------------------------- 1 | import AdminSidebar from "../../components/AdminSidebar"; 2 | import { useState, useEffect } from "react"; 3 | 4 | const formatTime = (timeInSeconds: number) => { 5 | const hours = Math.floor(timeInSeconds / 3600); 6 | const minutes = Math.floor((timeInSeconds % 3600) / 60); 7 | const seconds = timeInSeconds % 60; 8 | 9 | const hoursInString = hours.toString().padStart(2, "0"); 10 | const minutesInString = minutes.toString().padStart(2, "0"); 11 | const secondsInString = seconds.toString().padStart(2, "0"); 12 | 13 | return `${hoursInString}:${minutesInString}:${secondsInString}`; 14 | }; 15 | 16 | const Stopwatch = () => { 17 | const [time, setTime] = useState(0); 18 | const [isRunning, setIsRunning] = useState(false); 19 | 20 | const resetHandler = () => { 21 | setTime(0); 22 | setIsRunning(false); 23 | }; 24 | 25 | useEffect(() => { 26 | let intervalID: number; 27 | if (isRunning) 28 | intervalID = setInterval(() => { 29 | setTime((prev) => prev + 1); 30 | }, 1000); 31 | 32 | return () => { 33 | clearInterval(intervalID); 34 | }; 35 | }, [isRunning]); 36 | 37 | return ( 38 |
39 | 40 |
41 |

Stopwatch

42 |
43 |
44 |

{formatTime(time)}

45 | 48 | 49 |
50 |
51 |
52 |
53 | ); 54 | }; 55 | 56 | export default Stopwatch; 57 | -------------------------------------------------------------------------------- /src/pages/Transaction.tsx: -------------------------------------------------------------------------------- 1 | import { Column } from "react-table"; 2 | import AdminSidebar from "../components/AdminSidebar"; 3 | import { ReactElement, useState, useCallback } from "react"; 4 | import TableHOC from "../components/TableHOC"; 5 | import { Link } from "react-router-dom"; 6 | 7 | interface DataType { 8 | user: string; 9 | amount: number; 10 | discount: number; 11 | quantity: number; 12 | status: ReactElement; 13 | action: ReactElement; 14 | } 15 | 16 | const columns: Column[] = [ 17 | { 18 | Header: "Avatar", 19 | accessor: "user", 20 | }, 21 | { 22 | Header: "Amount", 23 | accessor: "amount", 24 | }, 25 | { 26 | Header: "Discount", 27 | accessor: "discount", 28 | }, 29 | { 30 | Header: "Quantity", 31 | accessor: "quantity", 32 | }, 33 | { 34 | Header: "Status", 35 | accessor: "status", 36 | }, 37 | { 38 | Header: "Action", 39 | accessor: "action", 40 | }, 41 | ]; 42 | 43 | const arr: DataType[] = [ 44 | { 45 | user: "Charas", 46 | amount: 4500, 47 | discount: 400, 48 | quantity: 3, 49 | status: Processing, 50 | action: Manage, 51 | }, 52 | { 53 | user: "Xavirors", 54 | amount: 6999, 55 | discount: 400, 56 | status: Shipped, 57 | quantity: 6, 58 | action: Manage, 59 | }, 60 | { 61 | user: "Xavirors", 62 | amount: 6999, 63 | discount: 400, 64 | status: Delivered, 65 | quantity: 6, 66 | action: Manage, 67 | }, 68 | ]; 69 | 70 | const Transaction = () => { 71 | const [data] = useState(arr); 72 | 73 | const Table = useCallback( 74 | TableHOC( 75 | columns, 76 | data, 77 | "dashboard-product-box", 78 | "Transactions", 79 | true 80 | ), 81 | [] 82 | ); 83 | 84 | return ( 85 |
86 | 87 |
{Table()}
88 |
89 | ); 90 | }; 91 | 92 | export default Transaction; 93 | -------------------------------------------------------------------------------- /src/pages/charts/LineCharts.tsx: -------------------------------------------------------------------------------- 1 | import AdminSidebar from "../../components/AdminSidebar"; 2 | import { LineChart } from "../../components/Charts"; 3 | 4 | const months = [ 5 | "January", 6 | "February", 7 | "March", 8 | "April", 9 | "May", 10 | "June", 11 | "July", 12 | "Aug", 13 | "Sept", 14 | "Oct", 15 | "Nov", 16 | "Dec", 17 | ]; 18 | 19 | const BarCharts = () => { 20 | return ( 21 |
22 | 23 |
24 |

Line Charts

25 |
26 | 35 |

Active Users

36 |
37 |
38 | 45 |

Total Products (SKU)

46 |
47 | 48 |
49 | 59 |

Total Revenue

60 |
61 | 62 |
63 | 73 |

Discount Allotted

74 |
75 |
76 |
77 | ); 78 | }; 79 | 80 | export default BarCharts; 81 | -------------------------------------------------------------------------------- /src/pages/management/NewProduct.tsx: -------------------------------------------------------------------------------- 1 | import { useState, ChangeEvent } from "react"; 2 | import AdminSidebar from "../../components/AdminSidebar"; 3 | 4 | const NewProduct = () => { 5 | const [name, setName] = useState(""); 6 | const [price, setPrice] = useState(); 7 | const [stock, setStock] = useState(); 8 | const [photo, setPhoto] = useState(); 9 | 10 | const changeImageHandler = (e: ChangeEvent) => { 11 | const file: File | undefined = e.target.files?.[0]; 12 | 13 | const reader: FileReader = new FileReader(); 14 | 15 | if (file) { 16 | reader.readAsDataURL(file); 17 | reader.onloadend = () => { 18 | if (typeof reader.result === "string") setPhoto(reader.result); 19 | }; 20 | } 21 | }; 22 | 23 | return ( 24 |
25 | 26 |
27 |
28 |
29 |

New Product

30 |
31 | 32 | setName(e.target.value)} 38 | /> 39 |
40 |
41 | 42 | setPrice(Number(e.target.value))} 48 | /> 49 |
50 |
51 | 52 | setStock(Number(e.target.value))} 58 | /> 59 |
60 | 61 |
62 | 63 | 64 |
65 | 66 | {photo && New Image} 67 | 68 | 69 |
70 |
71 |
72 |
73 | ); 74 | }; 75 | 76 | export default NewProduct; 77 | -------------------------------------------------------------------------------- /src/pages/Customers.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from "react"; 2 | import AdminSidebar from "../components/AdminSidebar"; 3 | import { Column } from "react-table"; 4 | import { useState, useCallback } from "react"; 5 | import TableHOC from "../components/TableHOC"; 6 | import { FaTrash } from "react-icons/fa"; 7 | 8 | interface DataType { 9 | avatar: ReactElement; 10 | name: string; 11 | email: string; 12 | gender: string; 13 | role: string; 14 | action: ReactElement; 15 | } 16 | 17 | const columns: Column[] = [ 18 | { 19 | Header: "Avatar", 20 | accessor: "avatar", 21 | }, 22 | { 23 | Header: "Name", 24 | accessor: "name", 25 | }, 26 | { 27 | Header: "Gender", 28 | accessor: "gender", 29 | }, 30 | { 31 | Header: "Email", 32 | accessor: "email", 33 | }, 34 | { 35 | Header: "Role", 36 | accessor: "role", 37 | }, 38 | { 39 | Header: "Action", 40 | accessor: "action", 41 | }, 42 | ]; 43 | const img = "https://randomuser.me/api/portraits/women/54.jpg"; 44 | const img2 = "https://randomuser.me/api/portraits/women/50.jpg"; 45 | 46 | const arr: DataType[] = [ 47 | { 48 | avatar: ( 49 | Shoes 56 | ), 57 | name: "Emily Palmer", 58 | email: "emily.palmer@example.com", 59 | gender: "female", 60 | role: "user", 61 | action: ( 62 | 65 | ), 66 | }, 67 | 68 | { 69 | avatar: ( 70 | Shoes 77 | ), 78 | name: "May Scoot", 79 | email: "aunt.may@example.com", 80 | gender: "female", 81 | role: "user", 82 | action: ( 83 | 86 | ), 87 | }, 88 | ]; 89 | 90 | const Customers = () => { 91 | const [data] = useState(arr); 92 | 93 | const Table = useCallback( 94 | TableHOC( 95 | columns, 96 | data, 97 | "dashboard-product-box", 98 | "Customers", 99 | true 100 | ), 101 | [] 102 | ); 103 | 104 | return ( 105 |
106 | 107 |
{Table()}
108 |
109 | ); 110 | }; 111 | 112 | export default Customers; 113 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom"; 2 | import { Suspense, lazy } from "react"; 3 | import Loader from "./components/Loader"; 4 | 5 | const Dashboard = lazy(() => import("./pages/Dashboard")); 6 | const Products = lazy(() => import("./pages/Products")); 7 | const Transaction = lazy(() => import("./pages/Transaction")); 8 | const Customers = lazy(() => import("./pages/Customers")); 9 | const NewProduct = lazy(() => import("./pages/management/NewProduct")); 10 | const ProductManagement = lazy( 11 | () => import("./pages/management/ProductManagement") 12 | ); 13 | const TransactionManagement = lazy( 14 | () => import("./pages/management/TransactionManagement") 15 | ); 16 | 17 | const BarCharts = lazy(() => import("./pages/charts/BarCharts")); 18 | const LineCharts = lazy(() => import("./pages/charts/LineCharts")); 19 | const PieCharts = lazy(() => import("./pages/charts/PieCharts")); 20 | 21 | const Stopwatch = lazy(() => import("./pages/apps/Stopwatch")); 22 | const Coupon = lazy(() => import("./pages/apps/Coupon")); 23 | const Toss = lazy(() => import("./pages/apps/Toss")); 24 | 25 | const App = () => { 26 | return ( 27 | 28 | }> 29 | 30 | 34 | 35 | 36 | } 37 | /> 38 | 39 | } /> 40 | } /> 41 | } /> 42 | } /> 43 | 44 | {/* Charts */} 45 | 46 | } /> 47 | } /> 48 | } /> 49 | 50 | {/* Apps */} 51 | 52 | } /> 53 | } /> 54 | } /> 55 | 56 | {/* Management */} 57 | } /> 58 | } /> 59 | } 62 | /> 63 | 64 | 65 | 66 | ); 67 | }; 68 | 69 | export default App; 70 | -------------------------------------------------------------------------------- /src/components/TableHOC.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AiOutlineSortAscending, 3 | AiOutlineSortDescending, 4 | } from "react-icons/ai"; 5 | import { 6 | Column, 7 | usePagination, 8 | useSortBy, 9 | useTable, 10 | TableOptions, 11 | } from "react-table"; 12 | 13 | function TableHOC( 14 | columns: Column[], 15 | data: T[], 16 | containerClassname: string, 17 | heading: string, 18 | showPagination: boolean = false 19 | ) { 20 | return function HOC() { 21 | const options: TableOptions = { 22 | columns, 23 | data, 24 | initialState: { 25 | pageSize: 6, 26 | }, 27 | }; 28 | 29 | const { 30 | getTableProps, 31 | getTableBodyProps, 32 | headerGroups, 33 | page, 34 | prepareRow, 35 | nextPage, 36 | pageCount, 37 | state: { pageIndex }, 38 | previousPage, 39 | canNextPage, 40 | canPreviousPage, 41 | } = useTable(options, useSortBy, usePagination); 42 | 43 | return ( 44 |
45 |

{heading}

46 | 47 | 48 | 49 | {headerGroups.map((headerGroup) => ( 50 | 51 | {headerGroup.headers.map((column) => ( 52 | 65 | ))} 66 | 67 | ))} 68 | 69 | 70 | {page.map((row) => { 71 | prepareRow(row); 72 | 73 | return ( 74 | 75 | {row.cells.map((cell) => ( 76 | 77 | ))} 78 | 79 | ); 80 | })} 81 | 82 |
53 | {column.render("Header")} 54 | {column.isSorted && ( 55 | 56 | {" "} 57 | {column.isSortedDesc ? ( 58 | 59 | ) : ( 60 | 61 | )} 62 | 63 | )} 64 |
{cell.render("Cell")}
83 | 84 | {showPagination && ( 85 |
86 | 89 | {`${pageIndex + 1} of ${pageCount}`} 90 | 93 |
94 | )} 95 |
96 | ); 97 | }; 98 | } 99 | 100 | export default TableHOC; 101 | -------------------------------------------------------------------------------- /src/pages/Products.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useCallback, useState } from "react"; 2 | import AdminSidebar from "../components/AdminSidebar"; 3 | import TableHOC from "../components/TableHOC"; 4 | import { Column } from "react-table"; 5 | import { Link } from "react-router-dom"; 6 | import { FaPlus } from "react-icons/fa"; 7 | 8 | interface DataType { 9 | photo: ReactElement; 10 | name: string; 11 | price: number; 12 | stock: number; 13 | action: ReactElement; 14 | } 15 | 16 | const columns: Column[] = [ 17 | { 18 | Header: "Photo", 19 | accessor: "photo", 20 | }, 21 | { 22 | Header: "Name", 23 | accessor: "name", 24 | }, 25 | { 26 | Header: "Price", 27 | accessor: "price", 28 | }, 29 | { 30 | Header: "Stock", 31 | accessor: "stock", 32 | }, 33 | { 34 | Header: "Action", 35 | accessor: "action", 36 | }, 37 | ]; 38 | 39 | const img = 40 | "https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8c2hvZXN8ZW58MHx8MHx8&w=1000&q=804"; 41 | 42 | const img2 = "https://m.media-amazon.com/images/I/514T0SvwkHL._SL1500_.jpg"; 43 | 44 | const arr: DataType[] = [ 45 | { 46 | photo: Shoes, 47 | name: "Puma Shoes Air Jordan Cook Nigga 2023", 48 | price: 690, 49 | stock: 3, 50 | action: Manage, 51 | }, 52 | 53 | { 54 | photo: Shoes, 55 | name: "Macbook", 56 | price: 232223, 57 | stock: 213, 58 | action: Manage, 59 | }, 60 | { 61 | photo: Shoes, 62 | name: "Puma Shoes Air Jordan Cook Nigga 2023", 63 | price: 690, 64 | stock: 3, 65 | action: Manage, 66 | }, 67 | 68 | { 69 | photo: Shoes, 70 | name: "Macbook", 71 | price: 232223, 72 | stock: 213, 73 | action: Manage, 74 | }, 75 | { 76 | photo: Shoes, 77 | name: "Puma Shoes Air Jordan Cook Nigga 2023", 78 | price: 690, 79 | stock: 3, 80 | action: Manage, 81 | }, 82 | 83 | { 84 | photo: Shoes, 85 | name: "Macbook", 86 | price: 232223, 87 | stock: 213, 88 | action: Manage, 89 | }, 90 | { 91 | photo: Shoes, 92 | name: "Macbook", 93 | price: 232223, 94 | stock: 213, 95 | action: Manage, 96 | }, 97 | ]; 98 | 99 | const Products = () => { 100 | const [data] = useState(arr); 101 | 102 | const Table = useCallback( 103 | TableHOC( 104 | columns, 105 | data, 106 | "dashboard-product-box", 107 | "Products", 108 | true 109 | ), 110 | [] 111 | ); 112 | 113 | return ( 114 |
115 | 116 |
{Table()}
117 | 118 | 119 | 120 |
121 | ); 122 | }; 123 | 124 | export default Products; 125 | -------------------------------------------------------------------------------- /src/styles/_dashboardapp.scss: -------------------------------------------------------------------------------- 1 | .dashboard-app-container { 2 | background-color: white; 3 | padding: 4rem; 4 | 5 | > section { 6 | @include flex(column, center, center, 2rem); 7 | height: 100%; 8 | 9 | .stopwatch { 10 | > h2 { 11 | font-size: 2rem; 12 | font-weight: 300; 13 | text-align: center; 14 | } 15 | 16 | > button { 17 | padding: 1rem 2rem; 18 | border: none; 19 | cursor: pointer; 20 | color: white; 21 | margin: 2rem; 22 | font-weight: 700; 23 | border-radius: 10px; 24 | 25 | &:first-of-type { 26 | background-color: rgb(0, 98, 255); 27 | } 28 | 29 | &:last-of-type { 30 | background-color: rgb(255, 0, 0); 31 | } 32 | } 33 | } 34 | 35 | .tosscoin { 36 | margin: 2rem; 37 | @include square(15rem); 38 | position: relative; 39 | cursor: pointer; 40 | transform-style: preserve-3d; 41 | transition: all 0.5s; 42 | > div { 43 | border-radius: 50%; 44 | @include square(100%); 45 | position: absolute; 46 | display: grid; 47 | place-items: center; 48 | background-repeat: no-repeat; 49 | background-size: contain; 50 | backface-visibility: hidden; 51 | filter: drop-shadow(0px 10px 10px rgba(0, 0, 0, 0.521)); 52 | &:first-of-type { 53 | background-image: url("../assets/images/heads.png"); 54 | } 55 | &:last-of-type { 56 | background-image: url("../assets/images/tails.png"); 57 | transform: rotateY(-180deg); 58 | } 59 | } 60 | } 61 | 62 | .coupon-form { 63 | @include grid(2fr 1fr, 2rem); 64 | max-width: 30rem; 65 | width: 100%; 66 | > input { 67 | padding: 1rem; 68 | border: 1px solid rgba(0, 0, 0, 0.359); 69 | outline: none; 70 | border-radius: 5px; 71 | } 72 | 73 | > fieldset { 74 | padding: 1rem; 75 | border: 1px solid rgba(0, 0, 0, 0.359); 76 | border-radius: 5px; 77 | @include flex(row, center, center, 0); 78 | flex-wrap: wrap; 79 | grid-column: 1/3; 80 | 81 | span { 82 | font-size: 0.8rem; 83 | font-weight: 300; 84 | margin-inline-start: 0.25rem; 85 | margin-inline-end: 1rem; 86 | } 87 | } 88 | 89 | > button { 90 | font-weight: 700; 91 | font-size: 1.1rem; 92 | width: 100%; 93 | padding: 1rem; 94 | border: none; 95 | color: white; 96 | cursor: pointer; 97 | margin: 2rem 0; 98 | border-radius: 10px; 99 | grid-column: 1/3; 100 | background-color: rgb(0, 98, 255); 101 | } 102 | } 103 | 104 | > code { 105 | position: relative; 106 | font-size: 1.2rem; 107 | letter-spacing: 2px; 108 | cursor: pointer; 109 | &:hover > span { 110 | opacity: 1; 111 | } 112 | 113 | > span { 114 | opacity: 0; 115 | @include square(100%); 116 | top: 0; 117 | left: 0; 118 | position: absolute; 119 | border-radius: 5px; 120 | background-color: rgb(15, 15, 15); 121 | color: white; 122 | font-size: 0.8rem; 123 | @include flex; 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/pages/management/ProductManagement.tsx: -------------------------------------------------------------------------------- 1 | import { useState, ChangeEvent, FormEvent } from "react"; 2 | import AdminSidebar from "../../components/AdminSidebar"; 3 | 4 | const img = 5 | "https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8c2hvZXN8ZW58MHx8MHx8&w=1000&q=804"; 6 | 7 | const ProductManagement = () => { 8 | const [name, setName] = useState("Puma Shoes"); 9 | const [price, setPrice] = useState(2000); 10 | const [stock, setStock] = useState(10); 11 | const [photo, setPhoto] = useState(img); 12 | 13 | const [nameUpdate, setNameUpdate] = useState(name); 14 | const [priceUpdate, setPriceUpdate] = useState(price); 15 | const [stockUpdate, setStockUpdate] = useState(stock); 16 | const [photoUpdate, setPhotoUpdate] = useState(photo); 17 | 18 | const changeImageHandler = (e: ChangeEvent) => { 19 | const file: File | undefined = e.target.files?.[0]; 20 | 21 | const reader: FileReader = new FileReader(); 22 | 23 | if (file) { 24 | reader.readAsDataURL(file); 25 | reader.onloadend = () => { 26 | if (typeof reader.result === "string") setPhotoUpdate(reader.result); 27 | }; 28 | } 29 | }; 30 | 31 | const submitHandler = (e: FormEvent) => { 32 | e.preventDefault(); 33 | setName(nameUpdate); 34 | setPrice(priceUpdate); 35 | setStock(stockUpdate); 36 | setPhoto(photoUpdate); 37 | }; 38 | return ( 39 |
40 | 41 |
42 |
43 | ID - asnmdkasndmsan 44 | Product 45 |

{name}

46 | {stock > 0 ? ( 47 | {stock} Available 48 | ) : ( 49 | Not Available 50 | )} 51 |

${price}

52 |
53 | 54 |
55 |
56 |

Manage

57 |
58 | 59 | setNameUpdate(e.target.value)} 65 | /> 66 |
67 |
68 | 69 | setPriceUpdate(Number(e.target.value))} 75 | /> 76 |
77 |
78 | 79 | setStockUpdate(Number(e.target.value))} 85 | /> 86 |
87 | 88 |
89 | 90 | 91 |
92 | 93 | {photoUpdate && New Image} 94 | 95 | 96 |
97 |
98 |
99 |
100 | ); 101 | }; 102 | 103 | export default ProductManagement; 104 | -------------------------------------------------------------------------------- /src/pages/management/TransactionManagement.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import AdminSidebar from "../../components/AdminSidebar"; 3 | import { OrderItemType, OrderType } from "../../types"; 4 | import { Link } from "react-router-dom"; 5 | 6 | const img = 7 | "https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8c2hvZXN8ZW58MHx8MHx8&w=1000&q=804"; 8 | 9 | const orderItems: OrderItemType[] = [ 10 | { 11 | name: "Puma Shoes", 12 | photo: img, 13 | _id: "asdsaasdas", 14 | quantity: 4, 15 | price: 2000, 16 | }, 17 | ]; 18 | 19 | const TransactionManagement = () => { 20 | const [order, setOrder] = useState({ 21 | name: "Abhishek Singh", 22 | address: "77 Black Street", 23 | city: "Neyword", 24 | state: "Nevada", 25 | country: "India", 26 | pinCode: 2434341, 27 | status: "Processing", 28 | subtotal: 4000, 29 | discount: 1200, 30 | shippingCharges: 0, 31 | tax: 200, 32 | total: 4000 + 200 + 0 - 1200, 33 | orderItems, 34 | _id: "asdnasjdhbn", 35 | }); 36 | 37 | const { 38 | name, 39 | address, 40 | city, 41 | country, 42 | state, 43 | pinCode, 44 | subtotal, 45 | shippingCharges, 46 | tax, 47 | discount, 48 | total, 49 | status, 50 | } = order; 51 | 52 | const updateHander = () => { 53 | setOrder((prev) => ({ 54 | ...prev, 55 | status: prev.status === "Processing" ? "Shipped" : "Delivered", 56 | })); 57 | }; 58 | 59 | return ( 60 |
61 | 62 |
63 |
68 |

Order Items

69 | 70 | {order.orderItems.map((i) => ( 71 | 78 | ))} 79 |
80 | 81 |
82 |

Order Info

83 |
User Info
84 |

Name: {name}

85 |

86 | Address: {`${address}, ${city}, ${state}, ${country} ${pinCode}`} 87 |

88 | 89 |
Amount Info
90 |

Subtotal: {subtotal}

91 |

Shipping Charges: {shippingCharges}

92 |

Tax: {tax}

93 |

Discount: {discount}

94 |

Total: {total}

95 | 96 |
Status Info
97 |

98 | Status:{" "} 99 | 108 | {status} 109 | 110 |

111 | 112 | 113 |
114 |
115 |
116 | ); 117 | }; 118 | 119 | const ProductCard = ({ name, photo, price, quantity, _id }: OrderItemType) => ( 120 |
121 | {name} 122 | {name} 123 | 124 | ${price} X {quantity} = ${price * quantity} 125 | 126 |
127 | ); 128 | 129 | export default TransactionManagement; 130 | -------------------------------------------------------------------------------- /src/pages/charts/PieCharts.tsx: -------------------------------------------------------------------------------- 1 | import AdminSidebar from "../../components/AdminSidebar"; 2 | import { DoughnutChart, PieChart } from "../../components/Charts"; 3 | import { categories } from "../../assets/data.json"; 4 | 5 | const PieCharts = () => { 6 | return ( 7 |
8 | 9 |
10 |

Pie & Doughnut Charts

11 |
12 |
13 | 23 |
24 |

Order Fulfillment Ratio

25 |
26 | 27 |
28 |
29 | i.heading)} 31 | data={categories.map((i) => i.value)} 32 | backgroundColor={categories.map( 33 | (i) => `hsl(${i.value * 4},${i.value}%, 50%)` 34 | )} 35 | legends={false} 36 | offset={[0, 0, 0, 80]} 37 | /> 38 |
39 |

Product Categories Ratio

40 |
41 | 42 |
43 |
44 | 52 |
53 |

Stock Availability

54 |
55 |
56 |
57 | 76 |
77 |

Revenue Distribution

78 |
79 | 80 |
81 |
82 | 96 |
97 |

Users Age Group

98 |
99 | 100 |
101 |
102 | 108 |
109 |
110 |
111 |
112 | ); 113 | }; 114 | 115 | export default PieCharts; 116 | -------------------------------------------------------------------------------- /src/styles/_products.scss: -------------------------------------------------------------------------------- 1 | .dashboard-product-box { 2 | background-color: white; 3 | padding: 2rem; 4 | overflow: auto; 5 | @include square(100%); 6 | } 7 | 8 | .create-product-btn { 9 | position: fixed; 10 | right: 2rem; 11 | top: 2rem; 12 | @include square(2.5rem); 13 | @include flex(row, center, center, 0); 14 | border-radius: 50%; 15 | background-color: rgb(201, 9, 9); 16 | color: white; 17 | &:hover { 18 | opacity: 0.8; 19 | } 20 | } 21 | 22 | .product-management { 23 | @include flex(row, center, unset); 24 | padding: 4rem; 25 | 26 | > section { 27 | overflow-y: auto; 28 | width: 100%; 29 | height: 85vh; 30 | max-width: 500px; 31 | box-shadow: 0px 5px 5px rgba(0, 0, 0, 0.216); 32 | background-color: white; 33 | padding: 5rem; 34 | @include flex(column, unset, unset, 1rem); 35 | position: relative; 36 | border-radius: 5px; 37 | 38 | > h2 { 39 | @include heading(2px); 40 | text-align: center; 41 | } 42 | 43 | > img { 44 | @include square(100%); 45 | object-fit: cover; 46 | } 47 | 48 | > strong { 49 | font-weight: 300; 50 | } 51 | 52 | > span { 53 | position: absolute; 54 | right: 2rem; 55 | top: 2rem; 56 | } 57 | 58 | > p { 59 | text-align: center; 60 | letter-spacing: 2px; 61 | text-transform: uppercase; 62 | } 63 | > h3 { 64 | text-align: center; 65 | font-size: 2rem; 66 | } 67 | } 68 | 69 | > article { 70 | height: 85vh; 71 | padding: 2rem; 72 | width: 100%; 73 | max-width: 400px; 74 | background-color: white; 75 | border-radius: 5px; 76 | box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.216); 77 | 78 | > form { 79 | @include flex(column, unset, center, 2rem); 80 | > h2 { 81 | text-transform: uppercase; 82 | letter-spacing: 2px; 83 | } 84 | 85 | > img { 86 | @include square(5rem); 87 | object-fit: cover; 88 | border-radius: 5px; 89 | } 90 | 91 | > div { 92 | width: 100%; 93 | position: relative; 94 | > label { 95 | position: absolute; 96 | left: 0; 97 | top: -1.5rem; 98 | } 99 | 100 | > input { 101 | @include inputStyle( 102 | 100%, 103 | 1rem, 104 | unset, 105 | 1px solid rgba(13, 13, 13, 0.196) 106 | ); 107 | border-radius: 5px; 108 | } 109 | } 110 | 111 | > button { 112 | padding: 1rem; 113 | border: none; 114 | background-color: rgb(5, 107, 224); 115 | color: white; 116 | width: 100%; 117 | border-radius: 5px; 118 | font-size: 1.1rem; 119 | cursor: pointer; 120 | } 121 | } 122 | } 123 | 124 | .shipping-info-card { 125 | > h1 { 126 | text-align: center; 127 | @include heading(2px); 128 | } 129 | > h5 { 130 | margin: 2rem 0 0 0.5rem; 131 | font-size: 1.1rem; 132 | font-weight: 700; 133 | } 134 | > p { 135 | margin: 0.5rem; 136 | } 137 | > button { 138 | margin: 2rem 0; 139 | padding: 1rem; 140 | border: none; 141 | background-color: rgb(5, 107, 224); 142 | color: white; 143 | width: 100%; 144 | border-radius: 5px; 145 | font-size: 1.1rem; 146 | cursor: pointer; 147 | &:hover { 148 | opacity: 0.8; 149 | } 150 | } 151 | } 152 | } 153 | 154 | .transaction-product-card { 155 | @include flex(row, unset, center, 1rem); 156 | 157 | > img { 158 | @include square(4rem); 159 | object-fit: cover; 160 | border-radius: 5px; 161 | } 162 | > span { 163 | margin-left: auto; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/pages/apps/Coupon.tsx: -------------------------------------------------------------------------------- 1 | import { FormEvent, useEffect, useState } from "react"; 2 | import AdminSidebar from "../../components/AdminSidebar"; 3 | 4 | const allLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 5 | const allNumbers = "1234567890"; 6 | const allSymbols = "!@#$%^&*()_+"; 7 | 8 | const Coupon = () => { 9 | const [size, setSize] = useState(8); 10 | const [prefix, setPrefix] = useState(""); 11 | const [includeNumbers, setIncludeNumbers] = useState(false); 12 | const [includeCharacters, setIncludeCharacters] = useState(false); 13 | const [includeSymbols, setIncludeSymbols] = useState(false); 14 | const [isCopied, setIsCopied] = useState(false); 15 | 16 | const [coupon, setCoupon] = useState(""); 17 | 18 | const copyText = async (coupon: string) => { 19 | await window.navigator.clipboard.writeText(coupon); 20 | setIsCopied(true); 21 | }; 22 | 23 | const submitHandler = (e: FormEvent) => { 24 | e.preventDefault(); 25 | 26 | if (!includeNumbers && !includeCharacters && !includeSymbols) 27 | return alert("Please Select One At Least"); 28 | 29 | let result: string = prefix || ""; 30 | const loopLength: number = size - result.length; 31 | 32 | for (let i = 0; i < loopLength; i++) { 33 | let entireString: string = ""; 34 | if (includeCharacters) entireString += allLetters; 35 | if (includeNumbers) entireString += allNumbers; 36 | if (includeSymbols) entireString += allSymbols; 37 | 38 | const randomNum: number = ~~(Math.random() * entireString.length); 39 | result += entireString[randomNum]; 40 | } 41 | 42 | setCoupon(result); 43 | }; 44 | 45 | useEffect(() => { 46 | setIsCopied(false); 47 | }, [coupon]); 48 | 49 | return ( 50 |
51 | 52 |
53 |

Coupon

54 |
55 |
56 | setPrefix(e.target.value)} 61 | maxLength={size} 62 | /> 63 | 64 | setSize(Number(e.target.value))} 69 | min={8} 70 | max={25} 71 | /> 72 | 73 |
74 | Include 75 | 76 | setIncludeNumbers((prev) => !prev)} 80 | /> 81 | Numbers 82 | 83 | setIncludeCharacters((prev) => !prev)} 87 | /> 88 | Characters 89 | 90 | setIncludeSymbols((prev) => !prev)} 94 | /> 95 | Symbols 96 |
97 | 98 |
99 | 100 | {coupon && ( 101 | 102 | {coupon}{" "} 103 | copyText(coupon)}> 104 | {isCopied ? "Copied" : "Copy"} 105 | {" "} 106 | 107 | )} 108 |
109 |
110 |
111 | ); 112 | }; 113 | 114 | export default Coupon; 115 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/_dashboard.scss: -------------------------------------------------------------------------------- 1 | .dashboard { 2 | overflow-y: auto; 3 | 4 | .bar { 5 | height: 4rem; 6 | @include flex(row, unset); 7 | padding: 0 1rem; 8 | border-bottom: 1px solid rgba(0, 0, 0, 0.37); 9 | > input { 10 | margin-right: auto; 11 | @include inputStyle(100%, 1rem 0); 12 | } 13 | > svg { 14 | font-size: 1.2rem; 15 | opacity: 0.7; 16 | } 17 | > img { 18 | @include square(2rem); 19 | border-radius: 50%; 20 | } 21 | } 22 | 23 | .widget-container { 24 | @include flex(row, space-between, stretch, 2rem); 25 | padding: 2rem 2rem 2rem 0; 26 | 27 | .widget { 28 | width: 16rem; 29 | background-color: white; 30 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.132); 31 | padding: 2rem; 32 | border-radius: 10px; 33 | @include flex(row, space-between, stretch, 0); 34 | 35 | .widget-info { 36 | > p { 37 | opacity: 0.7; 38 | font-size: 0.8rem; 39 | } 40 | > h4 { 41 | font-size: 1.5rem; 42 | } 43 | > span { 44 | @include flex(row, unset, center, 0.2rem); 45 | } 46 | } 47 | 48 | .widget-circle { 49 | position: relative; 50 | @include square(5rem); 51 | border-radius: 50%; 52 | flex: none; 53 | display: grid; 54 | place-items: center; 55 | background-color: aquamarine; 56 | &::before { 57 | content: ""; 58 | position: absolute; 59 | @include square(4rem); 60 | background-color: white; 61 | border-radius: 100%; 62 | } 63 | span { 64 | position: relative; 65 | } 66 | } 67 | } 68 | } 69 | 70 | .graph-container { 71 | @include flex(row, unset, unset, 2rem); 72 | padding: 0 2rem 2rem 0; 73 | > div { 74 | background-color: white; 75 | border-radius: 10px; 76 | } 77 | 78 | .revenue-chart { 79 | width: 100%; 80 | padding: 1rem 3rem; 81 | 82 | > h2 { 83 | @include heading; 84 | margin: 1rem 0 2rem 0.25rem; 85 | text-align: center; 86 | } 87 | } 88 | 89 | .dashboard-categories { 90 | width: 100%; 91 | max-width: 16rem; 92 | @include flex(column, center, unset, 0); 93 | padding-bottom: 2rem; 94 | > h2 { 95 | @include heading; 96 | margin: 1.5rem 0 2rem 0; 97 | text-align: center; 98 | } 99 | 100 | > div { 101 | overflow-y: auto; 102 | padding-left: 0.5rem; 103 | } 104 | 105 | .category-item { 106 | width: 100%; 107 | @include flex(row, space-between); 108 | padding: 1rem; 109 | > h5 { 110 | letter-spacing: 1px; 111 | font-weight: 300; 112 | } 113 | > div { 114 | margin-left: auto; 115 | width: 6rem; 116 | background-color: rgb(217, 217, 217); 117 | border-radius: 20px; 118 | height: 0.5rem; 119 | flex: none; 120 | > div { 121 | border-radius: 20px; 122 | height: 100%; 123 | } 124 | } 125 | > span { 126 | font-size: 0.8rem; 127 | font-weight: 700; 128 | } 129 | } 130 | } 131 | } 132 | 133 | .transaction-container { 134 | display: flex; 135 | gap: 2rem; 136 | padding: 0 2rem 2rem 0; 137 | height: 30rem; 138 | 139 | > div { 140 | background-color: white; 141 | box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.132); 142 | border-radius: 10px; 143 | } 144 | .gender-chart { 145 | width: 100%; 146 | max-width: 20rem; 147 | padding: 1rem; 148 | position: relative; 149 | > h2 { 150 | text-align: center; 151 | margin: 1.5rem 0 2rem 0; 152 | @include heading; 153 | } 154 | > p { 155 | @include posCenter; 156 | font-size: 2rem; 157 | color: rgba(0, 0, 0, 0.634); 158 | } 159 | } 160 | 161 | .transaction-box { 162 | width: 100%; 163 | padding: 1rem; 164 | overflow-x: auto; 165 | > h2 { 166 | margin: 1.5rem 0 0 0; 167 | @include heading; 168 | } 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/react-table-config.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | UseColumnOrderInstanceProps, 3 | UseColumnOrderState, 4 | UseExpandedHooks, 5 | UseExpandedInstanceProps, 6 | UseExpandedOptions, 7 | UseExpandedRowProps, 8 | UseExpandedState, 9 | UseFiltersColumnOptions, 10 | UseFiltersColumnProps, 11 | UseFiltersInstanceProps, 12 | UseFiltersOptions, 13 | UseFiltersState, 14 | UseGlobalFiltersColumnOptions, 15 | UseGlobalFiltersInstanceProps, 16 | UseGlobalFiltersOptions, 17 | UseGlobalFiltersState, 18 | UseGroupByCellProps, 19 | UseGroupByColumnOptions, 20 | UseGroupByColumnProps, 21 | UseGroupByHooks, 22 | UseGroupByInstanceProps, 23 | UseGroupByOptions, 24 | UseGroupByRowProps, 25 | UseGroupByState, 26 | UsePaginationInstanceProps, 27 | UsePaginationOptions, 28 | UsePaginationState, 29 | UseResizeColumnsColumnOptions, 30 | UseResizeColumnsColumnProps, 31 | UseResizeColumnsOptions, 32 | UseResizeColumnsState, 33 | UseRowSelectHooks, 34 | UseRowSelectInstanceProps, 35 | UseRowSelectOptions, 36 | UseRowSelectRowProps, 37 | UseRowSelectState, 38 | UseRowStateCellProps, 39 | UseRowStateInstanceProps, 40 | UseRowStateOptions, 41 | UseRowStateRowProps, 42 | UseRowStateState, 43 | UseSortByColumnOptions, 44 | UseSortByColumnProps, 45 | UseSortByHooks, 46 | UseSortByInstanceProps, 47 | UseSortByOptions, 48 | UseSortByState, 49 | } from "react-table"; 50 | 51 | declare module "react-table" { 52 | // take this file as-is, or comment out the sections that don't apply to your plugin configuration 53 | 54 | export interface TableOptions> 55 | extends UseExpandedOptions, 56 | UseFiltersOptions, 57 | UseGlobalFiltersOptions, 58 | UseGroupByOptions, 59 | UsePaginationOptions, 60 | UseResizeColumnsOptions, 61 | UseRowSelectOptions, 62 | UseRowStateOptions, 63 | UseSortByOptions, 64 | // note that having Record here allows you to add anything to the options, this matches the spirit of the 65 | // underlying js library, but might be cleaner if it's replaced by a more specific type that matches your 66 | // feature set, this is a safe default. 67 | Record {} 68 | 69 | export interface Hooks< 70 | D extends Record = Record 71 | > extends UseExpandedHooks, 72 | UseGroupByHooks, 73 | UseRowSelectHooks, 74 | UseSortByHooks {} 75 | 76 | export interface TableInstance< 77 | D extends Record = Record 78 | > extends UseColumnOrderInstanceProps, 79 | UseExpandedInstanceProps, 80 | UseFiltersInstanceProps, 81 | UseGlobalFiltersInstanceProps, 82 | UseGroupByInstanceProps, 83 | UsePaginationInstanceProps, 84 | UseRowSelectInstanceProps, 85 | UseRowStateInstanceProps, 86 | UseSortByInstanceProps {} 87 | 88 | export interface TableState< 89 | D extends Record = Record 90 | > extends UseColumnOrderState, 91 | UseExpandedState, 92 | UseFiltersState, 93 | UseGlobalFiltersState, 94 | UseGroupByState, 95 | UsePaginationState, 96 | UseResizeColumnsState, 97 | UseRowSelectState, 98 | UseRowStateState, 99 | UseSortByState {} 100 | 101 | export interface ColumnInterface< 102 | D extends Record = Record 103 | > extends UseFiltersColumnOptions, 104 | UseGlobalFiltersColumnOptions, 105 | UseGroupByColumnOptions, 106 | UseResizeColumnsColumnOptions, 107 | UseSortByColumnOptions {} 108 | 109 | export interface ColumnInstance< 110 | D extends Record = Record 111 | > extends UseFiltersColumnProps, 112 | UseGroupByColumnProps, 113 | UseResizeColumnsColumnProps, 114 | UseSortByColumnProps {} 115 | 116 | export interface Cell< 117 | D extends Record = Record, 118 | V = any 119 | > extends UseGroupByCellProps, 120 | UseRowStateCellProps {} 121 | 122 | export interface Row< 123 | D extends Record = Record 124 | > extends UseExpandedRowProps, 125 | UseGroupByRowProps, 126 | UseRowSelectRowProps, 127 | UseRowStateRowProps {} 128 | } 129 | -------------------------------------------------------------------------------- /src/styles/app.scss: -------------------------------------------------------------------------------- 1 | @import "mixin"; 2 | @import "dashboard"; 3 | @import "products"; 4 | @import "chart"; 5 | @import "dashboardapp"; 6 | @import "mediaquery"; 7 | 8 | :root { 9 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 10 | line-height: 1.5; 11 | font-weight: 400; 12 | } 13 | 14 | * { 15 | margin: 0; 16 | padding: 0; 17 | box-sizing: border-box; 18 | } 19 | 20 | a { 21 | text-decoration: none; 22 | color: black; 23 | } 24 | 25 | .red { 26 | color: red; 27 | } 28 | .purple { 29 | color: rgb(47, 0, 255); 30 | } 31 | .green { 32 | color: rgb(0, 195, 0); 33 | } 34 | 35 | .admin-container { 36 | @include grid(1fr 4fr, 2rem); 37 | height: 100vh; 38 | background-color: rgb(247, 247, 247); 39 | > aside { 40 | width: 100%; 41 | background-color: white; 42 | padding: 1rem; 43 | z-index: 10; 44 | overflow-y: auto; 45 | &::-webkit-scrollbar { 46 | display: none; 47 | } 48 | 49 | > div { 50 | margin: 2rem 1rem; 51 | > h5 { 52 | @include heading(2px); 53 | opacity: 0.8; 54 | margin: 1rem 0; 55 | } 56 | > ul { 57 | @include flex(column, unset, unset, 0.5rem); 58 | list-style: none; 59 | > li { 60 | padding: 0.5rem 1rem; 61 | border-radius: 10px; 62 | a { 63 | color: rgba(0, 0, 0, 0.825); 64 | @include flex(row, unset); 65 | } 66 | } 67 | } 68 | } 69 | } 70 | } 71 | 72 | .table { 73 | border-collapse: collapse; 74 | width: 100%; 75 | th, 76 | td { 77 | padding: 8px; 78 | text-align: left; 79 | vertical-align: middle; 80 | } 81 | 82 | th { 83 | font-weight: bold; 84 | color: #0000009e; 85 | font-size: 1.1rem; 86 | font-weight: 400; 87 | padding: 2rem 1rem; 88 | } 89 | 90 | tbody { 91 | tr { 92 | box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.093); 93 | } 94 | 95 | td { 96 | padding: 1rem; 97 | 98 | img { 99 | width: 4rem; 100 | height: 4rem; 101 | object-fit: contain; 102 | border-radius: 10px; 103 | } 104 | a { 105 | text-decoration: none; 106 | background-color: rgba(44, 104, 255, 0.455); 107 | color: rgba(44, 104, 255); 108 | padding: 0.25rem 0.5rem; 109 | border-radius: 10px; 110 | } 111 | button { 112 | width: 2rem; 113 | height: 2rem; 114 | font-size: 1.1rem; 115 | border: none; 116 | outline: none; 117 | background-color: transparent; 118 | cursor: pointer; 119 | color: rgb(255, 44, 44); 120 | &:hover { 121 | opacity: 0.6; 122 | } 123 | } 124 | 125 | &:first-child { 126 | border-left: none; 127 | } 128 | 129 | &:last-child { 130 | border-right: none; 131 | } 132 | } 133 | } 134 | } 135 | 136 | .table-pagination { 137 | @include flex; 138 | padding: 2rem; 139 | > button { 140 | padding: 0.5rem 1rem; 141 | border: none; 142 | outline: none; 143 | border-radius: 10px; 144 | cursor: pointer; 145 | background-color: rgba(0, 115, 255); 146 | color: white; 147 | &:disabled { 148 | background-color: rgba(0, 115, 255, 0.1); 149 | cursor: not-allowed; 150 | } 151 | } 152 | } 153 | 154 | #hamburger { 155 | display: grid; 156 | place-items: center; 157 | @include square(3rem); 158 | border: none; 159 | outline: none; 160 | cursor: pointer; 161 | color: rgba(44, 104, 255); 162 | position: fixed; 163 | top: 1rem; 164 | left: 1rem; 165 | font-size: 2rem; 166 | background-color: white; 167 | border-radius: 50%; 168 | z-index: 9; 169 | } 170 | 171 | #close-sidebar { 172 | width: 80%; 173 | margin: 1rem auto; 174 | display: block; 175 | padding: 0.75rem; 176 | border: none; 177 | outline: none; 178 | cursor: pointer; 179 | background-color: rgb(168, 2, 2); 180 | color: white; 181 | border-radius: 10px; 182 | } 183 | 184 | .loader { 185 | width: 100%; 186 | height: 100vh; 187 | @include flex; 188 | > div { 189 | @include square(10rem); 190 | border-radius: 50%; 191 | border-top: 1rem solid rgb(43, 43, 43); 192 | border-left: 1rem solid rgb(43, 43, 43); 193 | border-right: 1rem solid #fff; 194 | border-bottom: 1rem solid #fff; 195 | animation: loading-animation 0.5s linear infinite; 196 | } 197 | } 198 | 199 | @keyframes loading-animation { 200 | to { 201 | transform: rotateZ(360deg); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/components/AdminSidebar.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { IconType } from "react-icons"; 3 | import { AiFillFileText } from "react-icons/ai"; 4 | import { 5 | FaChartBar, 6 | FaChartLine, 7 | FaChartPie, 8 | FaGamepad, 9 | FaStopwatch, 10 | } from "react-icons/fa"; 11 | import { HiMenuAlt4 } from "react-icons/hi"; 12 | import { IoIosPeople } from "react-icons/io"; 13 | import { 14 | RiCoupon3Fill, 15 | RiDashboardFill, 16 | RiShoppingBag3Fill, 17 | } from "react-icons/ri"; 18 | import { Link, Location, useLocation } from "react-router-dom"; 19 | 20 | const AdminSidebar = () => { 21 | const location = useLocation(); 22 | 23 | const [showModal, setShowModal] = useState(false); 24 | const [phoneActive, setPhoneActive] = useState( 25 | window.innerWidth < 1100 26 | ); 27 | 28 | const resizeHandler = () => { 29 | setPhoneActive(window.innerWidth < 1100); 30 | }; 31 | 32 | useEffect(() => { 33 | window.addEventListener("resize", resizeHandler); 34 | 35 | return () => { 36 | window.removeEventListener("resize", resizeHandler); 37 | }; 38 | }, []); 39 | 40 | return ( 41 | <> 42 | {phoneActive && ( 43 | 46 | )} 47 | 48 | 73 | 74 | ); 75 | }; 76 | 77 | const DivOne = ({ location }: { location: Location }) => ( 78 |
79 |
Dashboard
80 |
    81 |
  • 87 |
  • 93 |
  • 99 |
  • 105 |
106 |
107 | ); 108 | 109 | const DivTwo = ({ location }: { location: Location }) => ( 110 |
111 |
Charts
112 |
    113 |
  • 119 |
  • 125 |
  • 131 |
132 |
133 | ); 134 | 135 | const DivThree = ({ location }: { location: Location }) => ( 136 |
137 |
Apps
138 |
    139 |
  • 145 |
  • 151 |
  • 157 |
158 |
159 | ); 160 | 161 | interface LiProps { 162 | url: string; 163 | text: string; 164 | location: Location; 165 | Icon: IconType; 166 | } 167 | const Li = ({ url, text, location, Icon }: LiProps) => ( 168 |
  • 175 | 181 | 182 | {text} 183 | 184 |
  • 185 | ); 186 | 187 | export default AdminSidebar; 188 | -------------------------------------------------------------------------------- /src/pages/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | import { FaRegBell } from "react-icons/fa"; 2 | import AdminSidebar from "../components/AdminSidebar"; 3 | import { BsSearch } from "react-icons/bs"; 4 | import userImg from "../assets/userpic.png"; 5 | import { HiTrendingUp, HiTrendingDown } from "react-icons/hi"; 6 | import data from "../assets/data.json"; 7 | import { BarChart, DoughnutChart } from "../components/Charts"; 8 | import { BiMaleFemale } from "react-icons/bi"; 9 | import Table from "../components/DashboardTable"; 10 | 11 | const dashboard = () => { 12 | return ( 13 |
    14 | 15 |
    16 |
    17 | 18 | 19 | 20 | User 21 |
    22 | 23 |
    24 | 31 | 37 | 43 | 49 |
    50 | 51 |
    52 |
    53 |

    Revenue & Transaction

    54 | {/* Grapph here */} 55 | 63 |
    64 | 65 |
    66 |

    Inventory

    67 |
    68 | {data.categories.map((i) => ( 69 | 75 | ))} 76 |
    77 |
    78 |
    79 | 80 |
    81 |
    82 |

    Gender Ratio

    83 | 84 | 90 | 91 |

    92 | 93 |

    94 |
    95 | 96 | 97 | 98 | 99 | 100 | ); 101 | }; 102 | 103 | interface WidgetItemProps { 104 | heading: string; 105 | value: number; 106 | percent: number; 107 | color: string; 108 | amount?: boolean; 109 | } 110 | 111 | const WidgetItem = ({ 112 | heading, 113 | value, 114 | percent, 115 | color, 116 | amount = false, 117 | }: WidgetItemProps) => ( 118 |
    119 |
    120 |

    {heading}

    121 |

    {amount ? `$${value}` : value}

    122 | {percent > 0 ? ( 123 | 124 | +{percent}%{" "} 125 | 126 | ) : ( 127 | 128 | {percent}%{" "} 129 | 130 | )} 131 |
    132 | 133 |
    142 | 147 | {percent}% 148 | 149 |
    150 |
    151 | ); 152 | 153 | interface CategoryItemProps { 154 | color: string; 155 | value: number; 156 | heading: string; 157 | } 158 | 159 | const CategoryItem = ({ color, value, heading }: CategoryItemProps) => ( 160 |
    161 |
    {heading}
    162 |
    163 |
    169 |
    170 | {value}% 171 |
    172 | ); 173 | 174 | export default dashboard; 175 | -------------------------------------------------------------------------------- /src/components/Charts.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Chart as ChartJS, 3 | CategoryScale, 4 | LinearScale, 5 | BarElement, 6 | Title, 7 | Tooltip, 8 | Legend, 9 | ChartData, 10 | ChartOptions, 11 | ArcElement, 12 | PointElement, 13 | LineElement, 14 | Filler, 15 | } from "chart.js"; 16 | import { Bar, Doughnut, Line, Pie } from "react-chartjs-2"; 17 | 18 | ChartJS.register( 19 | CategoryScale, 20 | LinearScale, 21 | BarElement, 22 | Title, 23 | Tooltip, 24 | Legend, 25 | ArcElement, 26 | PointElement, 27 | LineElement, 28 | Filler 29 | ); 30 | 31 | const months = ["January", "February", "March", "April", "May", "June", "July"]; 32 | 33 | interface BarChartProps { 34 | horizontal?: boolean; 35 | data_1: number[]; 36 | data_2: number[]; 37 | title_1: string; 38 | title_2: string; 39 | bgColor_1: string; 40 | bgColor_2: string; 41 | labels?: string[]; 42 | } 43 | 44 | export const BarChart = ({ 45 | data_1 = [], 46 | data_2 = [], 47 | title_1, 48 | title_2, 49 | bgColor_1, 50 | bgColor_2, 51 | horizontal = false, 52 | labels = months, 53 | }: BarChartProps) => { 54 | const options: ChartOptions<"bar"> = { 55 | responsive: true, 56 | indexAxis: horizontal ? "y" : "x", 57 | plugins: { 58 | legend: { 59 | display: false, 60 | }, 61 | title: { 62 | display: false, 63 | }, 64 | }, 65 | 66 | scales: { 67 | y: { 68 | beginAtZero: true, 69 | grid: { 70 | display: false, 71 | }, 72 | }, 73 | x: { 74 | grid: { 75 | display: false, 76 | }, 77 | }, 78 | }, 79 | }; 80 | 81 | const data: ChartData<"bar", number[], string> = { 82 | labels, 83 | datasets: [ 84 | { 85 | label: title_1, 86 | data: data_1, 87 | backgroundColor: bgColor_1, 88 | barThickness: "flex", 89 | barPercentage: 1, 90 | categoryPercentage: 0.4, 91 | }, 92 | { 93 | label: title_2, 94 | data: data_2, 95 | backgroundColor: bgColor_2, 96 | barThickness: "flex", 97 | barPercentage: 1, 98 | categoryPercentage: 0.4, 99 | }, 100 | ], 101 | }; 102 | 103 | return ; 104 | }; 105 | 106 | interface DoughnutChartProps { 107 | labels: string[]; 108 | data: number[]; 109 | backgroundColor: string[]; 110 | cutout?: number | string; 111 | legends?: boolean; 112 | offset?: number[]; 113 | } 114 | 115 | export const DoughnutChart = ({ 116 | labels, 117 | data, 118 | backgroundColor, 119 | cutout, 120 | legends = true, 121 | offset, 122 | }: DoughnutChartProps) => { 123 | const doughnutData: ChartData<"doughnut", number[], string> = { 124 | labels, 125 | datasets: [ 126 | { 127 | data, 128 | backgroundColor, 129 | borderWidth: 0, 130 | offset, 131 | }, 132 | ], 133 | }; 134 | 135 | const doughnutOptions: ChartOptions<"doughnut"> = { 136 | responsive: true, 137 | plugins: { 138 | legend: { 139 | display: legends, 140 | position: "bottom", 141 | labels: { 142 | padding: 40, 143 | }, 144 | }, 145 | }, 146 | cutout, 147 | }; 148 | 149 | return ; 150 | }; 151 | 152 | interface PieChartProps { 153 | labels: string[]; 154 | data: number[]; 155 | backgroundColor: string[]; 156 | offset?: number[]; 157 | } 158 | export const PieChart = ({ 159 | labels, 160 | data, 161 | backgroundColor, 162 | offset, 163 | }: PieChartProps) => { 164 | const pieChartData: ChartData<"pie", number[], string> = { 165 | labels, 166 | datasets: [ 167 | { 168 | data, 169 | backgroundColor, 170 | borderWidth: 1, 171 | offset, 172 | }, 173 | ], 174 | }; 175 | 176 | const pieChartOptions: ChartOptions<"pie"> = { 177 | responsive: true, 178 | plugins: { 179 | legend: { 180 | display: false, 181 | }, 182 | }, 183 | }; 184 | 185 | return ; 186 | }; 187 | 188 | interface LineChartProps { 189 | data: number[]; 190 | label: string; 191 | backgroundColor: string; 192 | borderColor: string; 193 | labels?: string[]; 194 | } 195 | 196 | export const LineChart = ({ 197 | data, 198 | label, 199 | backgroundColor, 200 | borderColor, 201 | labels = months, 202 | }: LineChartProps) => { 203 | const options: ChartOptions<"line"> = { 204 | responsive: true, 205 | plugins: { 206 | legend: { 207 | display: false, 208 | }, 209 | title: { 210 | display: false, 211 | }, 212 | }, 213 | 214 | scales: { 215 | y: { 216 | beginAtZero: true, 217 | grid: { 218 | display: false, 219 | }, 220 | }, 221 | x: { 222 | grid: { 223 | display: false, 224 | }, 225 | }, 226 | }, 227 | }; 228 | 229 | const lineChartData: ChartData<"line", number[], string> = { 230 | labels, 231 | datasets: [ 232 | { 233 | fill: true, 234 | label, 235 | data, 236 | backgroundColor, 237 | borderColor, 238 | }, 239 | ], 240 | }; 241 | 242 | return ; 243 | }; 244 | --------------------------------------------------------------------------------