├── .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 | 
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 | [](https://ko-fi.com/J3J1NMYT7)
84 |
85 | [](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 |
--------------------------------------------------------------------------------
/public/favicon-light.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
7 |
--------------------------------------------------------------------------------
/src/assets/logo-light.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
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 |
152 |
153 | {recentSalesData.map((sale) => (
154 |
158 |
159 |

164 |
165 |
{sale.name}
166 |
{sale.email}
167 |
168 |
169 |
${sale.total}
170 |
171 | ))}
172 |
173 |
174 |
175 |
176 |
179 |
180 |
181 |
182 |
183 |
184 | # |
185 | Product |
186 | Price |
187 | Status |
188 | Rating |
189 | Actions |
190 |
191 |
192 |
193 | {topProducts.map((product) => (
194 |
198 | {product.number} |
199 |
200 |
201 | 
206 |
207 | {product.name}
208 | {product.description}
209 |
210 |
211 | |
212 | ${product.price} |
213 | {product.status} |
214 |
215 |
216 |
220 | {product.rating}
221 |
222 | |
223 |
224 |
225 |
228 |
231 |
232 | |
233 |
234 | ))}
235 |
236 |
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 |
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 |
--------------------------------------------------------------------------------