├── .gitignore ├── .prettierrc ├── README.md ├── eslint.config.js ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── Dashboard UI Design.png ├── favicon-dark.svg └── favicon-light.svg ├── src ├── App.jsx ├── assets │ ├── logo-dark.svg │ ├── logo-light.svg │ ├── product-image.jpg │ └── profile-image.jpg ├── constants │ └── index.jsx ├── contexts │ └── theme-context.jsx ├── hooks │ ├── use-click-outside.jsx │ └── use-theme.jsx ├── index.css ├── layouts │ ├── footer.jsx │ ├── header.jsx │ └── sidebar.jsx ├── main.jsx ├── routes │ ├── dashboard │ │ └── page.jsx │ └── layout.jsx └── utils │ └── cn.js ├── tailwind.config.js └── vite.config.js /.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 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"], 3 | "singleAttributePerLine": true, 4 | "tabWidth": 4, 5 | "printWidth": 150 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |

7 | Build a Modern Dashboard UI Design using React JS, TailwindCSS. 8 |

9 | Watch Tutorial 10 | 11 |
12 | 13 |
14 | 15 | ![Dashboard UI Design](./public/Dashboard%20UI%20Design.png) 16 | 17 | ## 🗒️ Table of Contents 18 | 19 | 1. [💬 Introduction](#introduction) 20 | 2. [🛠️ Tools](#tools) 21 | 3. [✨ Features](#features) 22 | 4. [🚀 Getting Started](#getting-started) 23 | 5. [💻 Code Snippets](#code-snippets) 24 | 6. [☕ Support Me](#support-me) 25 | 26 | ## 💬 Introduction 27 | 28 | Welcome to the Dashboard UI Design repository! This project is a comprehensive guide to building a clean, user-friendly, and fully responsive dashboard interface. Whether you're new to UI/UX design or an experienced developer looking to enhance your skills, this repository provides essential resources to help you create an efficient and visually organized dashboard experience. Explore best practices for crafting intuitive layouts, managing data visualization, and ensuring seamless user interaction. 29 | 30 | ## 🛠️ Tools 31 | 32 | - [React JS](https://react.dev) 33 | - [TailwindCSS](https://tailwindcss.com/) 34 | - [Lucide Icons](https://lucide.dev/) 35 | - [React Router](https://reactrouter.com/en/main) 36 | - [Recharts](https://recharts.org/en-US/) 37 | 38 | ## ✨ Features 39 | 40 | - **Responsive Design:** Ensures your portfolio looks great on desktops, tablets, and mobile devices. 41 | - **Clean and Modern Layout:** A professional design that highlights your projects, skills, and experience. 42 | 43 | ## 🚀 Getting Started 44 | 45 | To get started follow these steps: 46 | 47 | #### Cloning the Repository 48 | 49 | Using CLI 50 | 51 | ```bash 52 | git clone https://github.com/xdcode2/dashboard-ui-design.git 53 | ``` 54 | 55 | **\*\*_Ensure you have installed [Git](https://git-scm.com) on your machine._** 56 | 57 | or using GitHub: 58 | 59 | - Go to the project [repository](https://github.com/xdcode2/dashboard-ui-design) on my GitHub page 60 | - Click on the green button on the top 👆 61 | - Click Download ZIP 62 | 63 | #### Installation 64 | 65 | Install the project dependencies using npm: 66 | 67 | ```bash 68 | npm install 69 | ``` 70 | 71 | **\*\*_Ensure you have installed [NodeJS](https://nodejs.org/en) on your machine._** 72 | 73 | #### Running the Project 74 | 75 | ```bash 76 | npm run dev 77 | ``` 78 | 79 | **\*\*_This project uses [Vite](https://vitejs.dev)._** 80 | 81 | ## ☕ Support Me 82 | 83 | [![ko-fi](https://img.shields.io/static/v1?message=Support%20me%20on%20ko-fi&logo=kofi&label=&color=ff5e5b&logoColor=white&labelColor=&style=for-the-badge)](https://ko-fi.com/J3J1NMYT7) 84 | 85 | [![youtube](https://img.shields.io/static/v1?message=Subscribe&logo=youtube&label=&color=FF0000&logoColor=white&labelColor=&style=for-the-badge)](https://www.youtube.com/@_xdcode_ "XD Code") 86 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import react from 'eslint-plugin-react' 4 | import reactHooks from 'eslint-plugin-react-hooks' 5 | import reactRefresh from 'eslint-plugin-react-refresh' 6 | 7 | export default [ 8 | { ignores: ['dist'] }, 9 | { 10 | files: ['**/*.{js,jsx}'], 11 | languageOptions: { 12 | ecmaVersion: 2020, 13 | globals: globals.browser, 14 | parserOptions: { 15 | ecmaVersion: 'latest', 16 | ecmaFeatures: { jsx: true }, 17 | sourceType: 'module', 18 | }, 19 | }, 20 | settings: { react: { version: '18.3' } }, 21 | plugins: { 22 | react, 23 | 'react-hooks': reactHooks, 24 | 'react-refresh': reactRefresh, 25 | }, 26 | rules: { 27 | ...js.configs.recommended.rules, 28 | ...react.configs.recommended.rules, 29 | ...react.configs['jsx-runtime'].rules, 30 | ...reactHooks.configs.recommended.rules, 31 | 'react/jsx-no-target-blank': 'off', 32 | 'react-refresh/only-export-components': [ 33 | 'warn', 34 | { allowConstantExport: true }, 35 | ], 36 | }, 37 | }, 38 | ] 39 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 15 | 21 | Dashboard UI Design | XD Code 22 | 23 | 24 |
25 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dashboard-ui-design", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@uidotdev/usehooks": "^2.4.1", 14 | "clsx": "^2.1.1", 15 | "lucide-react": "^0.441.0", 16 | "react": "^18.3.1", 17 | "react-dom": "^18.3.1", 18 | "react-router-dom": "^6.26.2", 19 | "recharts": "^2.12.7", 20 | "tailwind-merge": "^2.5.2" 21 | }, 22 | "devDependencies": { 23 | "@eslint/js": "^9.9.0", 24 | "@types/react": "^18.3.3", 25 | "@types/react-dom": "^18.3.0", 26 | "@vitejs/plugin-react": "^4.3.1", 27 | "autoprefixer": "^10.4.20", 28 | "eslint": "^9.9.0", 29 | "eslint-plugin-react": "^7.35.0", 30 | "eslint-plugin-react-hooks": "^5.1.0-rc.0", 31 | "eslint-plugin-react-refresh": "^0.4.9", 32 | "globals": "^15.9.0", 33 | "postcss": "^8.4.47", 34 | "prettier": "^3.3.3", 35 | "prettier-plugin-tailwindcss": "^0.6.6", 36 | "tailwindcss": "^3.4.11", 37 | "vite": "^5.4.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/Dashboard UI Design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdcode2/dashboard-ui-design/7b2351229465ae7d039a61437274e5b6af3f52be/public/Dashboard UI Design.png -------------------------------------------------------------------------------- /public/favicon-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/favicon-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { createBrowserRouter, RouterProvider } from "react-router-dom"; 2 | 3 | import { ThemeProvider } from "@/contexts/theme-context"; 4 | 5 | import Layout from "@/routes/layout"; 6 | import DashboardPage from "@/routes/dashboard/page"; 7 | 8 | function App() { 9 | const router = createBrowserRouter([ 10 | { 11 | path: "/", 12 | element: , 13 | children: [ 14 | { 15 | index: true, 16 | element: , 17 | }, 18 | { 19 | path: "analytics", 20 | element:

Analytics

, 21 | }, 22 | { 23 | path: "reports", 24 | element:

Reports

, 25 | }, 26 | { 27 | path: "customers", 28 | element:

Customers

, 29 | }, 30 | { 31 | path: "new-customer", 32 | element:

New Customer

, 33 | }, 34 | { 35 | path: "verified-customers", 36 | element:

Verified Customers

, 37 | }, 38 | { 39 | path: "products", 40 | element:

Products

, 41 | }, 42 | { 43 | path: "new-product", 44 | element:

New Product

, 45 | }, 46 | { 47 | path: "inventory", 48 | element:

Inventory

, 49 | }, 50 | { 51 | path: "settings", 52 | element:

Settings

, 53 | }, 54 | ], 55 | }, 56 | ]); 57 | 58 | return ( 59 | 60 | 61 | 62 | ); 63 | } 64 | 65 | export default App; 66 | -------------------------------------------------------------------------------- /src/assets/logo-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/logo-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/product-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdcode2/dashboard-ui-design/7b2351229465ae7d039a61437274e5b6af3f52be/src/assets/product-image.jpg -------------------------------------------------------------------------------- /src/assets/profile-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xdcode2/dashboard-ui-design/7b2351229465ae7d039a61437274e5b6af3f52be/src/assets/profile-image.jpg -------------------------------------------------------------------------------- /src/constants/index.jsx: -------------------------------------------------------------------------------- 1 | import { ChartColumn, Home, NotepadText, Package, PackagePlus, Settings, ShoppingBag, UserCheck, UserPlus, Users } from "lucide-react"; 2 | 3 | import ProfileImage from "@/assets/profile-image.jpg"; 4 | import ProductImage from "@/assets/product-image.jpg"; 5 | 6 | export const navbarLinks = [ 7 | { 8 | title: "Dashboard", 9 | links: [ 10 | { 11 | label: "Dashboard", 12 | icon: Home, 13 | path: "/", 14 | }, 15 | { 16 | label: "Analytics", 17 | icon: ChartColumn, 18 | path: "/analytics", 19 | }, 20 | { 21 | label: "Reports", 22 | icon: NotepadText, 23 | path: "/reports", 24 | }, 25 | ], 26 | }, 27 | { 28 | title: "Customers", 29 | links: [ 30 | { 31 | label: "Customers", 32 | icon: Users, 33 | path: "/customers", 34 | }, 35 | { 36 | label: "New customer", 37 | icon: UserPlus, 38 | path: "/new-customer", 39 | }, 40 | { 41 | label: "Verified customers", 42 | icon: UserCheck, 43 | path: "/verified-customers", 44 | }, 45 | ], 46 | }, 47 | { 48 | title: "Products", 49 | links: [ 50 | { 51 | label: "Products", 52 | icon: Package, 53 | path: "/products", 54 | }, 55 | { 56 | label: "New product", 57 | icon: PackagePlus, 58 | path: "/new-product", 59 | }, 60 | { 61 | label: "Inventory", 62 | icon: ShoppingBag, 63 | path: "/inventory", 64 | }, 65 | ], 66 | }, 67 | { 68 | title: "Settings", 69 | links: [ 70 | { 71 | label: "Settings", 72 | icon: Settings, 73 | path: "/settings", 74 | }, 75 | ], 76 | }, 77 | ]; 78 | 79 | export const overviewData = [ 80 | { 81 | name: "Jan", 82 | total: 1500, 83 | }, 84 | { 85 | name: "Feb", 86 | total: 2000, 87 | }, 88 | { 89 | name: "Mar", 90 | total: 1000, 91 | }, 92 | { 93 | name: "Apr", 94 | total: 5000, 95 | }, 96 | { 97 | name: "May", 98 | total: 2000, 99 | }, 100 | { 101 | name: "Jun", 102 | total: 5900, 103 | }, 104 | { 105 | name: "Jul", 106 | total: 2000, 107 | }, 108 | { 109 | name: "Aug", 110 | total: 5500, 111 | }, 112 | { 113 | name: "Sep", 114 | total: 2000, 115 | }, 116 | { 117 | name: "Oct", 118 | total: 4000, 119 | }, 120 | { 121 | name: "Nov", 122 | total: 1500, 123 | }, 124 | { 125 | name: "Dec", 126 | total: 2500, 127 | }, 128 | ]; 129 | 130 | export const recentSalesData = [ 131 | { 132 | id: 1, 133 | name: "Olivia Martin", 134 | email: "olivia.martin@email.com", 135 | image: ProfileImage, 136 | total: 1500, 137 | }, 138 | { 139 | id: 2, 140 | name: "James Smith", 141 | email: "james.smith@email.com", 142 | image: ProfileImage, 143 | total: 2000, 144 | }, 145 | { 146 | id: 3, 147 | name: "Sophia Brown", 148 | email: "sophia.brown@email.com", 149 | image: ProfileImage, 150 | total: 4000, 151 | }, 152 | { 153 | id: 4, 154 | name: "Noah Wilson", 155 | email: "noah.wilson@email.com", 156 | image: ProfileImage, 157 | total: 3000, 158 | }, 159 | { 160 | id: 5, 161 | name: "Emma Jones", 162 | email: "emma.jones@email.com", 163 | image: ProfileImage, 164 | total: 2500, 165 | }, 166 | { 167 | id: 6, 168 | name: "William Taylor", 169 | email: "william.taylor@email.com", 170 | image: ProfileImage, 171 | total: 4500, 172 | }, 173 | { 174 | id: 7, 175 | name: "Isabella Johnson", 176 | email: "isabella.johnson@email.com", 177 | image: ProfileImage, 178 | total: 5300, 179 | }, 180 | ]; 181 | 182 | export const topProducts = [ 183 | { 184 | number: 1, 185 | name: "Wireless Headphones", 186 | image: ProductImage, 187 | description: "High-quality noise-canceling wireless headphones.", 188 | price: 99.99, 189 | status: "In Stock", 190 | rating: 4.5, 191 | }, 192 | { 193 | number: 2, 194 | name: "Smartphone", 195 | image: ProductImage, 196 | description: "Latest 5G smartphone with excellent camera features.", 197 | price: 799.99, 198 | status: "In Stock", 199 | rating: 4.7, 200 | }, 201 | { 202 | number: 3, 203 | name: "Gaming Laptop", 204 | image: ProductImage, 205 | description: "Powerful gaming laptop with high-end graphics.", 206 | price: 1299.99, 207 | status: "In Stock", 208 | rating: 4.8, 209 | }, 210 | { 211 | number: 4, 212 | name: "Smartwatch", 213 | image: ProductImage, 214 | description: "Stylish smartwatch with fitness tracking features.", 215 | price: 199.99, 216 | status: "Out of Stock", 217 | rating: 4.4, 218 | }, 219 | { 220 | number: 5, 221 | name: "Bluetooth Speaker", 222 | image: ProductImage, 223 | description: "Portable Bluetooth speaker with deep bass sound.", 224 | price: 59.99, 225 | status: "In Stock", 226 | rating: 4.3, 227 | }, 228 | { 229 | number: 6, 230 | name: "4K Monitor", 231 | image: ProductImage, 232 | description: "Ultra HD 4K monitor with stunning color accuracy.", 233 | price: 399.99, 234 | status: "In Stock", 235 | rating: 4.6, 236 | }, 237 | { 238 | number: 7, 239 | name: "Mechanical Keyboard", 240 | image: ProductImage, 241 | description: "Mechanical keyboard with customizable RGB lighting.", 242 | price: 89.99, 243 | status: "In Stock", 244 | rating: 4.7, 245 | }, 246 | { 247 | number: 8, 248 | name: "Wireless Mouse", 249 | image: ProductImage, 250 | description: "Ergonomic wireless mouse with precision tracking.", 251 | price: 49.99, 252 | status: "In Stock", 253 | rating: 4.5, 254 | }, 255 | { 256 | number: 9, 257 | name: "Action Camera", 258 | image: ProductImage, 259 | description: "Waterproof action camera with 4K video recording.", 260 | price: 249.99, 261 | status: "In Stock", 262 | rating: 4.8, 263 | }, 264 | { 265 | number: 10, 266 | name: "External Hard Drive", 267 | image: ProductImage, 268 | description: "Portable 2TB external hard drive for data storage.", 269 | price: 79.99, 270 | status: "Out of Stock", 271 | rating: 4.5, 272 | }, 273 | ]; 274 | -------------------------------------------------------------------------------- /src/contexts/theme-context.jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useEffect, useState } from "react"; 2 | 3 | import PropTypes from "prop-types"; 4 | 5 | const initialState = { 6 | theme: "system", 7 | setTheme: () => null, 8 | }; 9 | 10 | export const ThemeProviderContext = createContext(initialState); 11 | 12 | export function ThemeProvider({ children, defaultTheme = "system", storageKey = "vite-ui-theme", ...props }) { 13 | const [theme, setTheme] = useState(() => localStorage.getItem(storageKey) || defaultTheme); 14 | 15 | useEffect(() => { 16 | const root = window.document.documentElement; 17 | 18 | root.classList.remove("light", "dark"); 19 | 20 | if (theme === "system") { 21 | const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; 22 | 23 | root.classList.add(systemTheme); 24 | return; 25 | } 26 | 27 | root.classList.add(theme); 28 | }, [theme]); 29 | 30 | const value = { 31 | theme, 32 | setTheme: (theme) => { 33 | localStorage.setItem(storageKey, theme); 34 | setTheme(theme); 35 | }, 36 | }; 37 | 38 | return ( 39 | 43 | {children} 44 | 45 | ); 46 | } 47 | 48 | ThemeProvider.propTypes = { 49 | children: PropTypes.node, 50 | defaultTheme: PropTypes.string, 51 | storageKey: PropTypes.string, 52 | }; 53 | -------------------------------------------------------------------------------- /src/hooks/use-click-outside.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | export const useClickOutside = (refs, callback) => { 4 | useEffect(() => { 5 | const handleOutsideClick = (event) => { 6 | const isOutside = refs.every((ref) => !ref?.current?.contains(event.target)); 7 | 8 | if (isOutside && typeof callback === "function") { 9 | callback(event); 10 | } 11 | }; 12 | 13 | window.addEventListener("mousedown", handleOutsideClick); 14 | 15 | return () => { 16 | window.removeEventListener("mousedown", handleOutsideClick); 17 | }; 18 | }, [callback, refs]); 19 | }; 20 | -------------------------------------------------------------------------------- /src/hooks/use-theme.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | 3 | import { ThemeProviderContext } from "@/contexts/theme-context"; 4 | 5 | export const useTheme = () => { 6 | const context = useContext(ThemeProviderContext); 7 | 8 | if (context === undefined) throw new Error("useTheme must be used within a ThemeProvider"); 9 | 10 | return context; 11 | }; 12 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | body { 7 | @apply [scrollbar-color:_#cbd5e1_transparent] dark:[scrollbar-color:_#334155_transparent]; 8 | } 9 | } 10 | 11 | @layer components { 12 | .sidebar-group { 13 | @apply flex w-full flex-col gap-y-2; 14 | } 15 | 16 | .sidebar-group-title { 17 | @apply overflow-hidden text-ellipsis text-sm font-medium text-slate-600 dark:text-slate-400; 18 | } 19 | 20 | .sidebar-item { 21 | @apply flex h-[40px] w-full flex-shrink-0 items-center gap-x-3 rounded-lg p-3 text-base font-medium text-slate-900 transition-colors hover:bg-blue-50 dark:text-slate-50 dark:hover:bg-blue-950; 22 | &.active { 23 | @apply bg-blue-500 text-slate-50 dark:bg-blue-600; 24 | } 25 | } 26 | 27 | .title { 28 | @apply text-3xl font-semibold text-slate-900 transition-colors dark:text-slate-50; 29 | } 30 | 31 | .btn-ghost { 32 | @apply flex h-10 flex-shrink-0 items-center justify-center gap-x-2 rounded-lg p-2 text-slate-400 transition-colors hover:bg-blue-50 hover:text-slate-500 dark:hover:bg-blue-950 dark:hover:text-slate-300; 33 | } 34 | 35 | .link { 36 | @apply text-base font-medium text-slate-900 transition-colors hover:underline dark:text-slate-50; 37 | } 38 | 39 | .input { 40 | @apply hidden h-10 flex-shrink-0 items-center gap-x-2 rounded-lg border border-slate-300 px-2 text-base text-slate-900 transition-colors has-[input:focus]:border-blue-500 md:flex md:w-auto lg:w-80 dark:border-slate-700 dark:text-slate-50 dark:focus:border-blue-600; 41 | } 42 | 43 | .card { 44 | @apply flex flex-col gap-y-4 rounded-lg border border-slate-300 bg-white p-4 transition-colors dark:border-slate-700 dark:bg-slate-900; 45 | } 46 | 47 | .card-header { 48 | @apply flex items-center gap-x-2; 49 | } 50 | 51 | .card-title { 52 | @apply font-medium text-slate-900 transition-colors dark:text-slate-50; 53 | } 54 | 55 | .card-body { 56 | @apply flex flex-col gap-y-2 rounded-lg p-4; 57 | } 58 | 59 | .table { 60 | @apply h-full w-full text-slate-900 dark:text-slate-50; 61 | } 62 | 63 | .table-header { 64 | @apply sticky top-0 bg-slate-200 transition-[background] dark:bg-slate-800; 65 | } 66 | 67 | .table-row { 68 | @apply border-b border-slate-300 transition-colors last:border-none dark:border-slate-700; 69 | } 70 | 71 | .table-head { 72 | @apply h-12 px-4 text-start; 73 | } 74 | 75 | .table-cell { 76 | @apply w-fit whitespace-nowrap p-4 font-medium; 77 | } 78 | } 79 | 80 | .recharts-default-tooltip { 81 | @apply !rounded-lg !border !border-slate-300 !bg-white transition-colors dark:!border-slate-700 dark:!bg-slate-900; 82 | } 83 | 84 | .recharts-tooltip-label { 85 | @apply text-base font-medium text-slate-900 dark:text-slate-50; 86 | } 87 | 88 | .recharts-tooltip-item { 89 | @apply text-base font-medium !text-blue-500 dark:!text-blue-600; 90 | } 91 | -------------------------------------------------------------------------------- /src/layouts/footer.jsx: -------------------------------------------------------------------------------- 1 | export const Footer = () => { 2 | return ( 3 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/layouts/header.jsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from "@/hooks/use-theme"; 2 | 3 | import { Bell, ChevronsLeft, Moon, Search, Sun } from "lucide-react"; 4 | 5 | import profileImg from "@/assets/profile-image.jpg"; 6 | 7 | import PropTypes from "prop-types"; 8 | 9 | export const Header = ({ collapsed, setCollapsed }) => { 10 | const { theme, setTheme } = useTheme(); 11 | 12 | return ( 13 |
14 |
15 | 21 |
22 | 26 | 33 |
34 |
35 |
36 | 49 | 52 | 59 |
60 |
61 | ); 62 | }; 63 | 64 | Header.propTypes = { 65 | collapsed: PropTypes.bool, 66 | setCollapsed: PropTypes.func, 67 | }; 68 | -------------------------------------------------------------------------------- /src/layouts/sidebar.jsx: -------------------------------------------------------------------------------- 1 | import { forwardRef } from "react"; 2 | import { NavLink } from "react-router-dom"; 3 | 4 | import { navbarLinks } from "@/constants"; 5 | 6 | import logoLight from "@/assets/logo-light.svg"; 7 | import logoDark from "@/assets/logo-dark.svg"; 8 | 9 | import { cn } from "@/utils/cn"; 10 | 11 | import PropTypes from "prop-types"; 12 | 13 | export const Sidebar = forwardRef(({ collapsed }, ref) => { 14 | return ( 15 | 60 | ); 61 | }); 62 | 63 | Sidebar.displayName = "Sidebar"; 64 | 65 | Sidebar.propTypes = { 66 | collapsed: PropTypes.bool, 67 | }; 68 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /src/routes/dashboard/page.jsx: -------------------------------------------------------------------------------- 1 | import { Area, AreaChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; 2 | 3 | import { useTheme } from "@/hooks/use-theme"; 4 | 5 | import { overviewData, recentSalesData, topProducts } from "@/constants"; 6 | 7 | import { Footer } from "@/layouts/footer"; 8 | 9 | import { CreditCard, DollarSign, Package, PencilLine, Star, Trash, TrendingUp, Users } from "lucide-react"; 10 | 11 | const DashboardPage = () => { 12 | const { theme } = useTheme(); 13 | 14 | return ( 15 |
16 |

Dashboard

17 |
18 |
19 |
20 |
21 | 22 |
23 |

Total Products

24 |
25 |
26 |

25,154

27 | 28 | 29 | 25% 30 | 31 |
32 |
33 |
34 |
35 |
36 | 37 |
38 |

Total Paid Orders

39 |
40 |
41 |

$16,000

42 | 43 | 44 | 12% 45 | 46 |
47 |
48 |
49 |
50 |
51 | 52 |
53 |

Total Customers

54 |
55 |
56 |

15,400k

57 | 58 | 59 | 15% 60 | 61 |
62 |
63 |
64 |
65 |
66 | 67 |
68 |

Sales

69 |
70 |
71 |

12,340

72 | 73 | 74 | 19% 75 | 76 |
77 |
78 |
79 |
80 |
81 |
82 |

Overview

83 |
84 |
85 | 89 | 98 | 99 | 106 | 111 | 116 | 117 | 118 | `$${value}`} 121 | /> 122 | 123 | 129 | `$${value}`} 134 | tickMargin={6} 135 | /> 136 | 137 | 144 | 145 | 146 |
147 |
148 |
149 |
150 |

Recent Sales

151 |
152 |
153 | {recentSalesData.map((sale) => ( 154 |
158 |
159 | {sale.name} 164 |
165 |

{sale.name}

166 |

{sale.email}

167 |
168 |
169 |

${sale.total}

170 |
171 | ))} 172 |
173 |
174 |
175 |
176 |
177 |

Top Orders

178 |
179 |
180 |
181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | {topProducts.map((product) => ( 194 | 198 | 199 | 212 | 213 | 214 | 223 | 233 | 234 | ))} 235 | 236 |
#ProductPriceStatusRatingActions
{product.number} 200 |
201 | {product.name} 206 |
207 |

{product.name}

208 |

{product.description}

209 |
210 |
211 |
${product.price}{product.status} 215 |
216 | 220 | {product.rating} 221 |
222 |
224 |
225 | 228 | 231 |
232 |
237 |
238 |
239 |
240 |
241 |
242 | ); 243 | }; 244 | 245 | export default DashboardPage; 246 | -------------------------------------------------------------------------------- /src/routes/layout.jsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router-dom"; 2 | 3 | import { useMediaQuery } from "@uidotdev/usehooks"; 4 | import { useClickOutside } from "@/hooks/use-click-outside"; 5 | 6 | import { Sidebar } from "@/layouts/sidebar"; 7 | import { Header } from "@/layouts/header"; 8 | 9 | import { cn } from "@/utils/cn"; 10 | import { useEffect, useRef, useState } from "react"; 11 | 12 | const Layout = () => { 13 | const isDesktopDevice = useMediaQuery("(min-width: 768px)"); 14 | const [collapsed, setCollapsed] = useState(!isDesktopDevice); 15 | 16 | const sidebarRef = useRef(null); 17 | 18 | useEffect(() => { 19 | setCollapsed(!isDesktopDevice); 20 | }, [isDesktopDevice]); 21 | 22 | useClickOutside([sidebarRef], () => { 23 | if (!isDesktopDevice && !collapsed) { 24 | setCollapsed(true); 25 | } 26 | }); 27 | 28 | return ( 29 |
30 |
36 | 40 |
41 |
45 |
46 | 47 |
48 |
49 |
50 | ); 51 | }; 52 | 53 | export default Layout; 54 | -------------------------------------------------------------------------------- /src/utils/cn.js: -------------------------------------------------------------------------------- 1 | import { clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | darkMode: "class", 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [], 9 | }; 10 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import path from "path"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | server: { 9 | port: 3000, 10 | }, 11 | resolve: { 12 | alias: { 13 | "@": path.resolve(__dirname, "./src"), 14 | }, 15 | }, 16 | }); 17 | --------------------------------------------------------------------------------