├── public └── _redirects ├── .gitignore ├── src ├── images │ ├── favicon.png │ ├── screenshot.png │ ├── screenshot2.png │ ├── screenshot3.png │ ├── screenshot4.png │ ├── screenshot5.png │ ├── user-36-01.jpg │ ├── user-36-02.jpg │ ├── user-36-03.jpg │ ├── user-36-04.jpg │ ├── user-36-05.jpg │ ├── user-36-06.jpg │ ├── user-36-07.jpg │ ├── user-36-08.jpg │ ├── user-36-09.jpg │ ├── user-avatar-32.png │ ├── icon-01.svg │ ├── icon-02.svg │ └── icon-03.svg ├── css │ ├── style.css │ ├── tailwind.config.js │ └── additional-styles │ │ ├── utility-patterns.css │ │ └── flatpickr.css ├── main.jsx ├── partials │ ├── SidebarLinkGroup.jsx │ ├── Banner.jsx │ ├── dashboard │ │ ├── DashboardCard04.jsx │ │ ├── DashboardCard06.jsx │ │ ├── DashboardAvatars.jsx │ │ ├── DashboardCard09.jsx │ │ ├── DashboardCard11.jsx │ │ ├── DashboardCard08.jsx │ │ ├── WelcomeBanner.jsx │ │ ├── DashboardCard05.jsx │ │ ├── DashboardCard10.jsx │ │ ├── DashboardCard01.jsx │ │ ├── DashboardCard02.jsx │ │ ├── DashboardCard03.jsx │ │ ├── DashboardCard13.jsx │ │ ├── DashboardCard07.jsx │ │ └── DashboardCard12.jsx │ └── Header.jsx ├── App.jsx ├── utils │ ├── Utils.js │ ├── ThemeContext.jsx │ ├── Info.jsx │ └── Transition.jsx ├── favicon.svg ├── components │ ├── Datepicker.jsx │ ├── ThemeToggle.jsx │ ├── DropdownEditMenu.jsx │ ├── Tooltip.jsx │ ├── DropdownProfile.jsx │ ├── DateSelect.jsx │ ├── DropdownHelp.jsx │ ├── DropdownNotifications.jsx │ ├── DropdownFilter.jsx │ └── ModalSearch.jsx ├── charts │ ├── ChartjsConfig.jsx │ ├── LineChart01.jsx │ ├── BarChart02.jsx │ ├── DoughnutChart.jsx │ ├── BarChart03.jsx │ ├── RealtimeChart.jsx │ ├── LineChart02.jsx │ └── BarChart01.jsx └── pages │ └── Dashboard.jsx ├── postcss.config.cjs ├── vite.config.js ├── package.json ├── index.html ├── CHANGELOG.md └── README.md /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local -------------------------------------------------------------------------------- /src/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/favicon.png -------------------------------------------------------------------------------- /src/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/screenshot.png -------------------------------------------------------------------------------- /src/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/screenshot2.png -------------------------------------------------------------------------------- /src/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/screenshot3.png -------------------------------------------------------------------------------- /src/images/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/screenshot4.png -------------------------------------------------------------------------------- /src/images/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/screenshot5.png -------------------------------------------------------------------------------- /src/images/user-36-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/user-36-01.jpg -------------------------------------------------------------------------------- /src/images/user-36-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/user-36-02.jpg -------------------------------------------------------------------------------- /src/images/user-36-03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/user-36-03.jpg -------------------------------------------------------------------------------- /src/images/user-36-04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/user-36-04.jpg -------------------------------------------------------------------------------- /src/images/user-36-05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/user-36-05.jpg -------------------------------------------------------------------------------- /src/images/user-36-06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/user-36-06.jpg -------------------------------------------------------------------------------- /src/images/user-36-07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/user-36-07.jpg -------------------------------------------------------------------------------- /src/images/user-36-08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/user-36-08.jpg -------------------------------------------------------------------------------- /src/images/user-36-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/user-36-09.jpg -------------------------------------------------------------------------------- /src/images/user-avatar-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daniel-16/market-hub/HEAD/src/images/user-avatar-32.png -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = ({ env }) => ({ 2 | plugins: [ 3 | require('tailwindcss')({ 4 | config: './src/css/tailwind.config.js' 5 | }), 6 | require('autoprefixer')() 7 | ], 8 | }) -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=fallback'); 2 | 3 | @import 'tailwindcss/base'; 4 | @import 'tailwindcss/components'; 5 | 6 | /* Additional styles */ 7 | @import 'additional-styles/utility-patterns.css'; 8 | @import 'additional-styles/flatpickr.css'; 9 | 10 | @import 'tailwindcss/utilities'; 11 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter as Router } from 'react-router-dom'; 4 | import ThemeProvider from './utils/ThemeContext'; 5 | import App from './App'; 6 | 7 | ReactDOM.createRoot(document.getElementById('root')).render( 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /src/partials/SidebarLinkGroup.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | function SidebarLinkGroup({ 4 | children, 5 | activecondition, 6 | }) { 7 | 8 | const [open, setOpen] = useState(activecondition); 9 | 10 | const handleClick = () => { 11 | setOpen(!open); 12 | } 13 | 14 | return ( 15 |
  • 16 | {children(handleClick, open)} 17 |
  • 18 | ); 19 | } 20 | 21 | export default SidebarLinkGroup; -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import postcss from './postcss.config.cjs' 3 | import react from '@vitejs/plugin-react' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | define: { 8 | 'process.env': process.env 9 | }, 10 | css: { 11 | postcss, 12 | }, 13 | plugins: [react()], 14 | resolve: { 15 | alias: [ 16 | { 17 | find: /^~.+/, 18 | replacement: (val) => { 19 | return val.replace(/^~/, ""); 20 | }, 21 | }, 22 | ], 23 | }, 24 | build: { 25 | commonjsOptions: { 26 | transformMixedEsModules: true, 27 | } 28 | } 29 | }) 30 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { 3 | Routes, 4 | Route, 5 | useLocation 6 | } from 'react-router-dom'; 7 | 8 | import './css/style.css'; 9 | 10 | import './charts/ChartjsConfig'; 11 | 12 | // Import pages 13 | import Dashboard from './pages/Dashboard'; 14 | 15 | function App() { 16 | 17 | const location = useLocation(); 18 | 19 | useEffect(() => { 20 | document.querySelector('html').style.scrollBehavior = 'auto' 21 | window.scroll({ top: 0 }) 22 | document.querySelector('html').style.scrollBehavior = '' 23 | }, [location.pathname]); // triggered on route change 24 | 25 | return ( 26 | <> 27 | 28 | } /> 29 | 30 | 31 | ); 32 | } 33 | 34 | export default App; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin-dashboard", 3 | "version": "0.1.0", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@tailwindcss/forms": "^0.5.7", 12 | "chart.js": "^4.4.1", 13 | "chartjs-adapter-moment": "^1.0.1", 14 | "moment": "^2.29.4", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0", 17 | "react-flatpickr": "^3.10.13", 18 | "react-router-dom": "^6.20.1", 19 | "react-transition-group": "^4.4.5" 20 | }, 21 | "devDependencies": { 22 | "@types/react": "^18.2.48", 23 | "@vitejs/plugin-react": "^4.2.1", 24 | "autoprefixer": "^10.4.16", 25 | "postcss": "^8.4.32", 26 | "tailwindcss": "^3.3.6", 27 | "vite": "^5.0.6" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/Utils.js: -------------------------------------------------------------------------------- 1 | import resolveConfig from 'tailwindcss/resolveConfig'; 2 | 3 | export const tailwindConfig = () => { 4 | // Tailwind config 5 | return resolveConfig('./src/css/tailwind.config.js') 6 | } 7 | 8 | export const hexToRGB = (h) => { 9 | let r = 0; 10 | let g = 0; 11 | let b = 0; 12 | if (h.length === 4) { 13 | r = `0x${h[1]}${h[1]}`; 14 | g = `0x${h[2]}${h[2]}`; 15 | b = `0x${h[3]}${h[3]}`; 16 | } else if (h.length === 7) { 17 | r = `0x${h[1]}${h[2]}`; 18 | g = `0x${h[3]}${h[4]}`; 19 | b = `0x${h[5]}${h[6]}`; 20 | } 21 | return `${+r},${+g},${+b}`; 22 | }; 23 | 24 | export const formatValue = (value) => Intl.NumberFormat('en-US', { 25 | style: 'currency', 26 | currency: 'USD', 27 | maximumSignificantDigits: 3, 28 | notation: 'compact', 29 | }).format(value); 30 | -------------------------------------------------------------------------------- /src/images/icon-01.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/images/icon-02.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/images/icon-03.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Admin Dashboard 8 | 17 | 18 | 21 | 22 | 29 |
    30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG.md 2 | 3 | ## [2.1.0] - 2023-12-08 4 | 5 | - Update Vite to 5 6 | - Update other dependencies 7 | 8 | ## [2.0.1] - 2023-10-04 9 | 10 | - Dependencies update 11 | 12 | ## [2.0.0] - 2023-06-01 13 | 14 | - Dark version added 15 | 16 | ## [1.7.4] - 2023-04-11 17 | 18 | - Update dependencies 19 | 20 | ## [1.7.3] - 2023-02-13 21 | 22 | - Further sidebar color improvements 23 | 24 | ## [1.7.2] - 2023-02-13 25 | 26 | - Update dependencies 27 | - Improve sidebar icons color logic 28 | 29 | ## [1.7.0] - 2022-08-30 30 | 31 | - Update sidebar 32 | - Fix mobile menu issue 33 | 34 | ## [1.6.0] - 2022-07-15 35 | 36 | - Replace Sass with CSS files 37 | - Update dependencies 38 | - Update React to v18 39 | 40 | ## [1.3.1] - 2022-01-27 41 | 42 | - Fix tailwind.config.js file 43 | 44 | ## [1.3.0] - 2022-01-25 45 | 46 | - Replace CRA (Create React App) with Vite 47 | - Remove Craco 48 | - Update dependencies 49 | 50 | ## [1.2.0] - 2021-12-13 51 | 52 | - Update Tailwind 3 53 | - Several improvements 54 | 55 | ## [1.1.1] - 2021-11-23 56 | 57 | - Alignment issue## [1.1.1] - 2021-11-23 58 | 59 | - Alignment issue 60 | 61 | ## [1.1.0] - 2021-11-15 62 | 63 | - Update dependencies 64 | - Enable Tailwind JIT 65 | - Add expandable / collapsible sidebar feature 66 | 67 | ## [1.0.0] - 2021-04-20 68 | 69 | First release -------------------------------------------------------------------------------- /src/utils/ThemeContext.jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useState, useEffect } from 'react'; 2 | 3 | const ThemeContext = createContext({ 4 | currentTheme: 'light', 5 | changeCurrentTheme: () => {}, 6 | }); 7 | 8 | export default function ThemeProvider({children}) { 9 | const persistedTheme = localStorage.getItem('theme'); 10 | const [theme, setTheme] = useState(persistedTheme || 'light'); 11 | 12 | const changeCurrentTheme = (newTheme) => { 13 | setTheme(newTheme); 14 | localStorage.setItem('theme', newTheme); 15 | }; 16 | 17 | useEffect(() => { 18 | document.documentElement.classList.add('[&_*]:!transition-none'); 19 | if (theme === 'light') { 20 | document.documentElement.classList.remove('dark'); 21 | document.documentElement.style.colorScheme = 'light'; 22 | } else { 23 | document.documentElement.classList.add('dark'); 24 | document.documentElement.style.colorScheme = 'dark'; 25 | } 26 | 27 | const transitionTimeout = setTimeout(() => { 28 | document.documentElement.classList.remove('[&_*]:!transition-none'); 29 | }, 1); 30 | 31 | return () => clearTimeout(transitionTimeout); 32 | }, [theme]); 33 | 34 | return {children}; 35 | } 36 | 37 | export const useThemeProvider = () => useContext(ThemeContext); -------------------------------------------------------------------------------- /src/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Admin Dashboard 2 | 3 | This is a modern and customizable admin dashboard built with Tailwind CSS and React. It features a clean and intuitive design, making it easy to manage your application's data and settings. This is basically a front end project 4 | 5 | [Project Demo](https://dan-codes-admin-dashboard.vercel.app/) 6 | 7 | ![Admin Dashboard](/src/images/screenshot.png) 8 | 9 | ![Admin Dashboard](/src/images/screenshot2.png) 10 | 11 | ![Admin Dashboard](/src/images/screenshot3.png) 12 | 13 | ![Admin Dashboard](/src/images/screenshot4.png) 14 | 15 | ![Admin Dashboard](/src/images/screenshot5.png) 16 | 17 | ## Features 18 | 19 | - Tailwind CSS for styling 20 | - React for state management and interactivity 21 | - Fully responsive design 22 | - Easy to customize to match your brand 23 | - Modular design for easy maintenance 24 | 25 | ## Getting Started 26 | 27 | 1. Clone the repository: 28 | 29 | ``` 30 | git clone https://github.com/Daniel-16/admin-dashboard.git 31 | ``` 32 | 33 | 2. Install the dependencies: 34 | 35 | ``` 36 | npm install 37 | ``` 38 | 39 | 3. Start the development server 40 | 41 | ``` 42 | npm run dev 43 | ``` 44 | 45 | 4. Open your browser and navigate to http://localhost:5173 to see the dashboard. 46 | 47 | ## Customization 48 | 49 | You can easily customize the dashboard to match your brand by editing the `tailwind.config.js` file. You can also change the colors, fonts, and other styles by editing the `styles.css` file. 50 | 51 | ## Contributing 52 | 53 | Contributions are welcome! Create a new branch for edits and create a pull request. 54 | 55 | ## License 56 | 57 | This project is licensed under the MIT License. 58 | -------------------------------------------------------------------------------- /src/utils/Info.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import Transition from './Transition'; 3 | 4 | function Info({ 5 | children, 6 | className, 7 | containerClassName 8 | }) { 9 | 10 | const [infoOpen, setInfoOpen] = useState(false); 11 | 12 | return ( 13 |
    setInfoOpen(true)} 16 | onMouseLeave={() => setInfoOpen(false)} 17 | onFocus={() => setInfoOpen(true)} 18 | onBlur={() => setInfoOpen(false)} 19 | > 20 | 30 |
    31 | 42 | {children} 43 | 44 |
    45 |
    46 | ); 47 | } 48 | 49 | export default Info; -------------------------------------------------------------------------------- /src/components/Datepicker.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Flatpickr from 'react-flatpickr'; 3 | 4 | function Datepicker({ 5 | align 6 | }) { 7 | 8 | const options = { 9 | mode: 'range', 10 | static: true, 11 | monthSelectorType: 'static', 12 | dateFormat: 'M j, Y', 13 | defaultDate: [new Date().setDate(new Date().getDate() - 6), new Date()], 14 | prevArrow: '', 15 | nextArrow: '', 16 | onReady: (selectedDates, dateStr, instance) => { 17 | instance.element.value = dateStr.replace('to', '-'); 18 | const customClass = (align) ? align : ''; 19 | instance.calendarContainer.classList.add(`flatpickr-${customClass}`); 20 | }, 21 | onChange: (selectedDates, dateStr, instance) => { 22 | instance.element.value = dateStr.replace('to', '-'); 23 | }, 24 | } 25 | 26 | return ( 27 |
    28 | 29 |
    30 | 31 | 32 | 33 |
    34 |
    35 | ); 36 | } 37 | 38 | export default Datepicker; 39 | -------------------------------------------------------------------------------- /src/partials/Banner.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | function Banner() { 4 | 5 | const [bannerOpen, setBannerOpen] = useState(true); 6 | const query = new URLSearchParams(location.search); 7 | const template = query.get('template'); 8 | const liteLink = 9 | template === 'laravel' 10 | ? 'https://github.com/cruip/laravel-tailwindcss-admin-dashboard-template' 11 | : 'https://github.com/cruip/tailwind-dashboard-template'; 12 | 13 | return ( 14 | <> 15 | { bannerOpen && ( 16 |
    17 |
    18 |
    Download on GitHub or Check Premium Version
    19 | 25 |
    26 |
    27 | )} 28 | 29 | ); 30 | } 31 | 32 | export default Banner; -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard04.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import BarChart from '../../charts/BarChart01'; 3 | 4 | // Import utilities 5 | import { tailwindConfig } from '../../utils/Utils'; 6 | 7 | function DashboardCard04() { 8 | 9 | const chartData = { 10 | labels: [ 11 | '12-01-2020', '01-01-2021', '02-01-2021', 12 | '03-01-2021', '04-01-2021', '05-01-2021', 13 | ], 14 | datasets: [ 15 | // Light blue bars 16 | { 17 | label: 'Direct', 18 | data: [ 19 | 800, 1600, 900, 1300, 1950, 1700, 20 | ], 21 | backgroundColor: tailwindConfig().theme.colors.blue[400], 22 | hoverBackgroundColor: tailwindConfig().theme.colors.blue[500], 23 | barPercentage: 0.66, 24 | categoryPercentage: 0.66, 25 | }, 26 | // Blue bars 27 | { 28 | label: 'Indirect', 29 | data: [ 30 | 4900, 2600, 5350, 4800, 5200, 4800, 31 | ], 32 | backgroundColor: tailwindConfig().theme.colors.indigo[500], 33 | hoverBackgroundColor: tailwindConfig().theme.colors.indigo[600], 34 | barPercentage: 0.66, 35 | categoryPercentage: 0.66, 36 | }, 37 | ], 38 | }; 39 | 40 | return ( 41 |
    42 |
    43 |

    Direct VS Indirect

    44 |
    45 | {/* Chart built with Chart.js 3 */} 46 | {/* Change the height attribute to adjust the chart height */} 47 | 48 |
    49 | ); 50 | } 51 | 52 | export default DashboardCard04; 53 | -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard06.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DoughnutChart from "../../charts/DoughnutChart"; 3 | 4 | // Import utilities 5 | import { tailwindConfig } from "../../utils/Utils"; 6 | 7 | function DashboardCard06() { 8 | const chartData = { 9 | labels: ["United States", "Italy", "Nigeria", "United Kingdom", "Others"], 10 | datasets: [ 11 | { 12 | label: "Top Countries", 13 | data: [30, 15, 25, 20, 10], 14 | backgroundColor: [ 15 | tailwindConfig().theme.colors.indigo[500], 16 | tailwindConfig().theme.colors.blue[400], 17 | tailwindConfig().theme.colors.indigo[800], 18 | tailwindConfig().theme.colors.red[800], 19 | tailwindConfig().theme.colors.green[800], 20 | ], 21 | hoverBackgroundColor: [ 22 | tailwindConfig().theme.colors.indigo[600], 23 | tailwindConfig().theme.colors.blue[500], 24 | tailwindConfig().theme.colors.indigo[900], 25 | tailwindConfig().theme.colors.red[900], 26 | tailwindConfig().theme.colors.green[900], 27 | ], 28 | borderWidth: 0, 29 | }, 30 | ], 31 | }; 32 | 33 | return ( 34 |
    35 |
    36 |

    37 | Top Countries 38 |

    39 |
    40 | {/* Chart built with Chart.js 3 */} 41 | {/* Change the height attribute to adjust the chart height */} 42 | 43 |
    44 | ); 45 | } 46 | 47 | export default DashboardCard06; 48 | -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardAvatars.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import User01 from '../../images/user-36-01.jpg'; 4 | import User02 from '../../images/user-36-02.jpg'; 5 | import User03 from '../../images/user-36-03.jpg'; 6 | import User04 from '../../images/user-36-04.jpg'; 7 | 8 | function DashboardAvatars() { 9 | return ( 10 | 40 | ); 41 | } 42 | 43 | export default DashboardAvatars; 44 | -------------------------------------------------------------------------------- /src/components/ThemeToggle.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useThemeProvider } from '../utils/ThemeContext'; 3 | 4 | export default function ThemeToggle() { 5 | const { currentTheme, changeCurrentTheme } = useThemeProvider(); 6 | 7 | return ( 8 |
    9 | changeCurrentTheme(currentTheme === 'light' ? 'dark' : 'light')} 16 | /> 17 | 40 |
    41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/css/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const plugin = require('tailwindcss/plugin'); 2 | 3 | module.exports = { 4 | content: [ 5 | './index.html', 6 | './src/**/*.{js,jsx,ts,tsx}', 7 | ], 8 | darkMode: 'class', 9 | theme: { 10 | extend: { 11 | boxShadow: { 12 | DEFAULT: '0 1px 3px 0 rgba(0, 0, 0, 0.08), 0 1px 2px 0 rgba(0, 0, 0, 0.02)', 13 | md: '0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.02)', 14 | lg: '0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 4px 6px -2px rgba(0, 0, 0, 0.01)', 15 | xl: '0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 10px 10px -5px rgba(0, 0, 0, 0.01)', 16 | }, 17 | outline: { 18 | blue: '2px solid rgba(0, 112, 244, 0.5)', 19 | }, 20 | fontFamily: { 21 | inter: ['Inter', 'sans-serif'], 22 | }, 23 | fontSize: { 24 | xs: ['0.75rem', { lineHeight: '1.5' }], 25 | sm: ['0.875rem', { lineHeight: '1.5715' }], 26 | base: ['1rem', { lineHeight: '1.5', letterSpacing: '-0.01em' }], 27 | lg: ['1.125rem', { lineHeight: '1.5', letterSpacing: '-0.01em' }], 28 | xl: ['1.25rem', { lineHeight: '1.5', letterSpacing: '-0.01em' }], 29 | '2xl': ['1.5rem', { lineHeight: '1.33', letterSpacing: '-0.01em' }], 30 | '3xl': ['1.88rem', { lineHeight: '1.33', letterSpacing: '-0.01em' }], 31 | '4xl': ['2.25rem', { lineHeight: '1.25', letterSpacing: '-0.02em' }], 32 | '5xl': ['3rem', { lineHeight: '1.25', letterSpacing: '-0.02em' }], 33 | '6xl': ['3.75rem', { lineHeight: '1.2', letterSpacing: '-0.02em' }], 34 | }, 35 | screens: { 36 | xs: '480px', 37 | }, 38 | borderWidth: { 39 | 3: '3px', 40 | }, 41 | minWidth: { 42 | 36: '9rem', 43 | 44: '11rem', 44 | 56: '14rem', 45 | 60: '15rem', 46 | 72: '18rem', 47 | 80: '20rem', 48 | }, 49 | maxWidth: { 50 | '8xl': '88rem', 51 | '9xl': '96rem', 52 | }, 53 | zIndex: { 54 | 60: '60', 55 | }, 56 | }, 57 | }, 58 | plugins: [ 59 | // eslint-disable-next-line global-require 60 | require('@tailwindcss/forms'), 61 | // add custom variant for expanding sidebar 62 | plugin(({ addVariant, e }) => { 63 | addVariant('sidebar-expanded', ({ modifySelectors, separator }) => { 64 | modifySelectors(({ className }) => `.sidebar-expanded .${e(`sidebar-expanded${separator}${className}`)}`); 65 | }); 66 | }), 67 | ], 68 | }; 69 | -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard09.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Tooltip from '../../components/Tooltip'; 3 | import BarChart from '../../charts/BarChart02'; 4 | 5 | // Import utilities 6 | import { tailwindConfig } from '../../utils/Utils'; 7 | 8 | function DashboardCard09() { 9 | 10 | const chartData = { 11 | labels: [ 12 | '12-01-2020', '01-01-2021', '02-01-2021', 13 | '03-01-2021', '04-01-2021', '05-01-2021', 14 | ], 15 | datasets: [ 16 | // Light blue bars 17 | { 18 | label: 'Stack 1', 19 | data: [ 20 | 6200, 9200, 6600, 8800, 5200, 9200, 21 | ], 22 | backgroundColor: tailwindConfig().theme.colors.indigo[500], 23 | hoverBackgroundColor: tailwindConfig().theme.colors.indigo[600], 24 | barPercentage: 0.66, 25 | categoryPercentage: 0.66, 26 | }, 27 | // Blue bars 28 | { 29 | label: 'Stack 2', 30 | data: [ 31 | -4000, -2600, -5350, -4000, -7500, -2000, 32 | ], 33 | backgroundColor: tailwindConfig().theme.colors.indigo[200], 34 | hoverBackgroundColor: tailwindConfig().theme.colors.indigo[300], 35 | barPercentage: 0.66, 36 | categoryPercentage: 0.66, 37 | }, 38 | ], 39 | }; 40 | 41 | return ( 42 |
    43 |
    44 |

    Sales VS Refunds

    45 | 46 |
    Sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit.
    47 |
    48 |
    49 |
    50 |
    51 |
    +$6,796
    52 |
    -34%
    53 |
    54 |
    55 | {/* Chart built with Chart.js 3 */} 56 |
    57 | {/* Change the height attribute to adjust the chart height */} 58 | 59 |
    60 |
    61 | ); 62 | } 63 | 64 | export default DashboardCard09; 65 | -------------------------------------------------------------------------------- /src/charts/ChartjsConfig.jsx: -------------------------------------------------------------------------------- 1 | // Import Chart.js 2 | import { Chart, Tooltip } from 'chart.js'; 3 | // Import Tailwind config 4 | import { tailwindConfig, hexToRGB } from '../utils/Utils'; 5 | 6 | Chart.register(Tooltip); 7 | 8 | // Define Chart.js default settings 9 | Chart.defaults.font.family = '"Inter", sans-serif'; 10 | Chart.defaults.font.weight = '500'; 11 | Chart.defaults.plugins.tooltip.borderWidth = 1; 12 | Chart.defaults.plugins.tooltip.displayColors = false; 13 | Chart.defaults.plugins.tooltip.mode = 'nearest'; 14 | Chart.defaults.plugins.tooltip.intersect = false; 15 | Chart.defaults.plugins.tooltip.position = 'nearest'; 16 | Chart.defaults.plugins.tooltip.caretSize = 0; 17 | Chart.defaults.plugins.tooltip.caretPadding = 20; 18 | Chart.defaults.plugins.tooltip.cornerRadius = 4; 19 | Chart.defaults.plugins.tooltip.padding = 8; 20 | 21 | // Register Chart.js plugin to add a bg option for chart area 22 | Chart.register({ 23 | id: 'chartAreaPlugin', 24 | // eslint-disable-next-line object-shorthand 25 | beforeDraw: (chart) => { 26 | if (chart.config.options.chartArea && chart.config.options.chartArea.backgroundColor) { 27 | const ctx = chart.canvas.getContext('2d'); 28 | const { chartArea } = chart; 29 | ctx.save(); 30 | ctx.fillStyle = chart.config.options.chartArea.backgroundColor; 31 | // eslint-disable-next-line max-len 32 | ctx.fillRect(chartArea.left, chartArea.top, chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); 33 | ctx.restore(); 34 | } 35 | }, 36 | }); 37 | 38 | export const chartColors = { 39 | textColor: { 40 | light: tailwindConfig().theme.colors.slate[400], 41 | dark: tailwindConfig().theme.colors.slate[500], 42 | }, 43 | gridColor: { 44 | light: tailwindConfig().theme.colors.slate[100], 45 | dark: tailwindConfig().theme.colors.slate[700], 46 | }, 47 | backdropColor: { 48 | light: tailwindConfig().theme.colors.white, 49 | dark: tailwindConfig().theme.colors.slate[800], 50 | }, 51 | tooltipTitleColor: { 52 | light: tailwindConfig().theme.colors.slate[800], 53 | dark: tailwindConfig().theme.colors.slate[100], 54 | }, 55 | tooltipBodyColor: { 56 | light: tailwindConfig().theme.colors.slate[800], 57 | dark: tailwindConfig().theme.colors.slate[100], 58 | }, 59 | tooltipBgColor: { 60 | light: tailwindConfig().theme.colors.white, 61 | dark: tailwindConfig().theme.colors.slate[700], 62 | }, 63 | tooltipBorderColor: { 64 | light: tailwindConfig().theme.colors.slate[200], 65 | dark: tailwindConfig().theme.colors.slate[600], 66 | }, 67 | chartAreaBg: { 68 | light: tailwindConfig().theme.colors.slate[50], 69 | dark: `rgba(${hexToRGB(tailwindConfig().theme.colors.slate[900])}, 0.24)`, 70 | }, 71 | }; 72 | -------------------------------------------------------------------------------- /src/components/DropdownEditMenu.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from 'react'; 2 | import Transition from '../utils/Transition'; 3 | 4 | function DropdownEditMenu({ 5 | children, 6 | align, 7 | ...rest 8 | }) { 9 | 10 | const [dropdownOpen, setDropdownOpen] = useState(false); 11 | 12 | const trigger = useRef(null); 13 | const dropdown = useRef(null); 14 | 15 | // close on click outside 16 | useEffect(() => { 17 | const clickHandler = ({ target }) => { 18 | if (!dropdown.current) return; 19 | if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return; 20 | setDropdownOpen(false); 21 | }; 22 | document.addEventListener('click', clickHandler); 23 | return () => document.removeEventListener('click', clickHandler); 24 | }); 25 | 26 | // close if the esc key is pressed 27 | useEffect(() => { 28 | const keyHandler = ({ keyCode }) => { 29 | if (!dropdownOpen || keyCode !== 27) return; 30 | setDropdownOpen(false); 31 | }; 32 | document.addEventListener('keydown', keyHandler); 33 | return () => document.removeEventListener('keydown', keyHandler); 34 | }); 35 | 36 | return ( 37 |
    38 | 56 | 69 | 72 | 73 |
    74 | ); 75 | } 76 | 77 | export default DropdownEditMenu; -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard11.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import BarChart from '../../charts/BarChart03'; 3 | 4 | // Import utilities 5 | import { tailwindConfig } from '../../utils/Utils'; 6 | 7 | function DashboardCard11() { 8 | 9 | const chartData = { 10 | labels: ['Reasons'], 11 | datasets: [ 12 | { 13 | label: 'Having difficulties using the product', 14 | data: [131], 15 | backgroundColor: tailwindConfig().theme.colors.indigo[500], 16 | hoverBackgroundColor: tailwindConfig().theme.colors.indigo[600], 17 | barPercentage: 1, 18 | categoryPercentage: 1, 19 | }, 20 | { 21 | label: 'Missing features I need', 22 | data: [100], 23 | backgroundColor: tailwindConfig().theme.colors.indigo[800], 24 | hoverBackgroundColor: tailwindConfig().theme.colors.indigo[900], 25 | barPercentage: 1, 26 | categoryPercentage: 1, 27 | }, 28 | { 29 | label: 'Not satisfied about the quality of the product', 30 | data: [81], 31 | backgroundColor: tailwindConfig().theme.colors.sky[400], 32 | hoverBackgroundColor: tailwindConfig().theme.colors.sky[500], 33 | barPercentage: 1, 34 | categoryPercentage: 1, 35 | }, 36 | { 37 | label: 'The product doesn’t look as advertised', 38 | data: [65], 39 | backgroundColor: tailwindConfig().theme.colors.green[400], 40 | hoverBackgroundColor: tailwindConfig().theme.colors.green[500], 41 | barPercentage: 1, 42 | categoryPercentage: 1, 43 | }, 44 | { 45 | label: 'Other', 46 | data: [72], 47 | backgroundColor: tailwindConfig().theme.colors.slate[200], 48 | hoverBackgroundColor: tailwindConfig().theme.colors.slate[300], 49 | barPercentage: 1, 50 | categoryPercentage: 1, 51 | }, 52 | ], 53 | }; 54 | 55 | return ( 56 |
    57 |
    58 |

    Reason for Refunds

    59 |
    60 |
    61 |
    62 |
    449
    63 |
    -22%
    64 |
    65 |
    66 | {/* Chart built with Chart.js 3 */} 67 |
    68 | {/* Change the height attribute to adjust the chart height */} 69 | 70 |
    71 |
    72 | ); 73 | } 74 | 75 | export default DashboardCard11; 76 | -------------------------------------------------------------------------------- /src/components/Tooltip.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import Transition from '../utils/Transition'; 3 | 4 | function Tooltip({ 5 | children, 6 | className, 7 | bg, 8 | size, 9 | position, 10 | }) { 11 | 12 | const [tooltipOpen, setTooltipOpen] = useState(false); 13 | 14 | const positionOuterClasses = (position) => { 15 | switch (position) { 16 | case 'right': 17 | return 'left-full top-1/2 -translate-y-1/2'; 18 | case 'left': 19 | return 'right-full top-1/2 -translate-y-1/2'; 20 | case 'bottom': 21 | return 'top-full left-1/2 -translate-x-1/2'; 22 | default: 23 | return 'bottom-full left-1/2 -translate-x-1/2'; 24 | } 25 | } 26 | 27 | const sizeClasses = (size) => { 28 | switch (size) { 29 | case 'lg': 30 | return 'min-w-72 p-3'; 31 | case 'md': 32 | return 'min-w-56 p-3'; 33 | case 'sm': 34 | return 'min-w-44 p-2'; 35 | default: 36 | return 'p-2'; 37 | } 38 | }; 39 | 40 | const colorClasses = (bg) => { 41 | switch (bg) { 42 | case 'light': 43 | return 'bg-white text-slate-600 border-slate-200'; 44 | case 'dark': 45 | return 'bg-slate-700 text-slate-100 border-slate-600'; 46 | default: 47 | return 'text-slate-600 bg-white dark:bg-slate-700 dark:text-slate-100 border-slate-200 dark:border-slate-600'; 48 | } 49 | }; 50 | 51 | const positionInnerClasses = (position) => { 52 | switch (position) { 53 | case 'right': 54 | return 'ml-2'; 55 | case 'left': 56 | return 'mr-2'; 57 | case 'bottom': 58 | return 'mt-2'; 59 | default: 60 | return 'mb-2'; 61 | } 62 | }; 63 | 64 | return ( 65 |
    setTooltipOpen(true)} 68 | onMouseLeave={() => setTooltipOpen(false)} 69 | onFocus={() => setTooltipOpen(true)} 70 | onBlur={() => setTooltipOpen(false)} 71 | > 72 | 77 |
    78 | 89 | {children} 90 | 91 |
    92 |
    93 | ); 94 | } 95 | 96 | export default Tooltip; -------------------------------------------------------------------------------- /src/partials/Header.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | import SearchModal from '../components/ModalSearch'; 4 | import Notifications from '../components/DropdownNotifications'; 5 | import Help from '../components/DropdownHelp'; 6 | import UserMenu from '../components/DropdownProfile'; 7 | import ThemeToggle from '../components/ThemeToggle'; 8 | 9 | function Header({ sidebarOpen, setSidebarOpen }) { 10 | const [searchModalOpen, setSearchModalOpen] = useState(false); 11 | 12 | return ( 13 |
    14 |
    15 |
    16 | {/* Header: Left side */} 17 |
    18 | {/* Hamburger button */} 19 | 35 |
    36 | 37 | {/* Header: Right side */} 38 |
    39 |
    40 | 62 | 63 |
    64 | 65 | 66 | 67 | {/* Divider */} 68 |
    69 | 70 |
    71 |
    72 |
    73 |
    74 | ); 75 | } 76 | 77 | export default Header; 78 | -------------------------------------------------------------------------------- /src/charts/LineChart01.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect, useState } from 'react'; 2 | import { useThemeProvider } from '../utils/ThemeContext'; 3 | 4 | import { chartColors } from './ChartjsConfig'; 5 | import { 6 | Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip, 7 | } from 'chart.js'; 8 | import 'chartjs-adapter-moment'; 9 | 10 | // Import utilities 11 | import { formatValue } from '../utils/Utils'; 12 | 13 | Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip); 14 | 15 | function LineChart01({ 16 | data, 17 | width, 18 | height 19 | }) { 20 | 21 | const [chart, setChart] = useState(null) 22 | const canvas = useRef(null); 23 | const { currentTheme } = useThemeProvider(); 24 | const darkMode = currentTheme === 'dark'; 25 | const { tooltipBodyColor, tooltipBgColor, tooltipBorderColor, chartAreaBg } = chartColors; 26 | 27 | useEffect(() => { 28 | const ctx = canvas.current; 29 | // eslint-disable-next-line no-unused-vars 30 | const newChart = new Chart(ctx, { 31 | type: 'line', 32 | data: data, 33 | options: { 34 | chartArea: { 35 | backgroundColor: darkMode ? chartAreaBg.dark : chartAreaBg.light, 36 | }, 37 | layout: { 38 | padding: 20, 39 | }, 40 | scales: { 41 | y: { 42 | display: false, 43 | beginAtZero: true, 44 | }, 45 | x: { 46 | type: 'time', 47 | time: { 48 | parser: 'MM-DD-YYYY', 49 | unit: 'month', 50 | }, 51 | display: false, 52 | }, 53 | }, 54 | plugins: { 55 | tooltip: { 56 | callbacks: { 57 | title: () => false, // Disable tooltip title 58 | label: (context) => formatValue(context.parsed.y), 59 | }, 60 | bodyColor: darkMode ? tooltipBodyColor.dark : tooltipBodyColor.light, 61 | backgroundColor: darkMode ? tooltipBgColor.dark : tooltipBgColor.light, 62 | borderColor: darkMode ? tooltipBorderColor.dark : tooltipBorderColor.light, 63 | }, 64 | legend: { 65 | display: false, 66 | }, 67 | }, 68 | interaction: { 69 | intersect: false, 70 | mode: 'nearest', 71 | }, 72 | maintainAspectRatio: false, 73 | resizeDelay: 200, 74 | }, 75 | }); 76 | setChart(newChart); 77 | return () => newChart.destroy(); 78 | // eslint-disable-next-line react-hooks/exhaustive-deps 79 | }, []); 80 | 81 | useEffect(() => { 82 | if (!chart) return; 83 | 84 | if (darkMode) { 85 | chart.options.chartArea.backgroundColor = chartAreaBg.dark; 86 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.dark; 87 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.dark; 88 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.dark; 89 | } else { 90 | chart.options.chartArea.backgroundColor = chartAreaBg.light; 91 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.light; 92 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.light; 93 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.light; 94 | } 95 | chart.update('none'); 96 | }, [currentTheme]); 97 | 98 | return ( 99 | 100 | ); 101 | } 102 | 103 | export default LineChart01; -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard08.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import LineChart from '../../charts/LineChart02'; 3 | 4 | // Import utilities 5 | import { tailwindConfig } from '../../utils/Utils'; 6 | 7 | function DashboardCard08() { 8 | 9 | const chartData = { 10 | labels: [ 11 | '12-01-2020', 12 | '01-01-2021', 13 | '02-01-2021', 14 | '03-01-2021', 15 | '04-01-2021', 16 | '05-01-2021', 17 | '06-01-2021', 18 | '07-01-2021', 19 | '08-01-2021', 20 | '09-01-2021', 21 | '10-01-2021', 22 | '11-01-2021', 23 | '12-01-2021', 24 | '01-01-2022', 25 | '02-01-2022', 26 | '03-01-2022', 27 | '04-01-2022', 28 | '05-01-2022', 29 | '06-01-2022', 30 | '07-01-2022', 31 | '08-01-2022', 32 | '09-01-2022', 33 | '10-01-2022', 34 | '11-01-2022', 35 | '12-01-2022', 36 | '01-01-2023', 37 | ], 38 | datasets: [ 39 | // Indigo line 40 | { 41 | label: 'Current', 42 | data: [73, 64, 73, 69, 104, 104, 164, 164, 120, 120, 120, 148, 142, 104, 122, 110, 104, 152, 166, 233, 268, 252, 284, 284, 333, 323], 43 | borderColor: tailwindConfig().theme.colors.indigo[500], 44 | fill: false, 45 | borderWidth: 2, 46 | tension: 0, 47 | pointRadius: 0, 48 | pointHoverRadius: 3, 49 | pointBackgroundColor: tailwindConfig().theme.colors.indigo[500], 50 | pointHoverBackgroundColor: tailwindConfig().theme.colors.indigo[500], 51 | pointBorderWidth: 0, 52 | pointHoverBorderWidth: 0, 53 | clip: 20, 54 | }, 55 | // Blue line 56 | { 57 | label: 'Previous', 58 | data: [184, 86, 42, 378, 42, 243, 38, 120, 0, 0, 42, 0, 84, 0, 276, 0, 124, 42, 124, 88, 88, 215, 156, 88, 124, 64], 59 | borderColor: tailwindConfig().theme.colors.blue[400], 60 | fill: false, 61 | borderWidth: 2, 62 | tension: 0, 63 | pointRadius: 0, 64 | pointHoverRadius: 3, 65 | pointBackgroundColor: tailwindConfig().theme.colors.blue[400], 66 | pointHoverBackgroundColor: tailwindConfig().theme.colors.blue[400], 67 | pointBorderWidth: 0, 68 | pointHoverBorderWidth: 0, 69 | clip: 20, 70 | }, 71 | // emerald line 72 | { 73 | label: 'Average', 74 | data: [122, 170, 192, 86, 102, 124, 115, 115, 56, 104, 0, 72, 208, 186, 223, 188, 114, 162, 200, 150, 118, 118, 76, 122, 230, 268], 75 | borderColor: tailwindConfig().theme.colors.emerald[500], 76 | fill: false, 77 | borderWidth: 2, 78 | tension: 0, 79 | pointRadius: 0, 80 | pointHoverRadius: 3, 81 | pointBackgroundColor: tailwindConfig().theme.colors.emerald[500], 82 | pointHoverBackgroundColor: tailwindConfig().theme.colors.emerald[500], 83 | pointBorderWidth: 0, 84 | pointHoverBorderWidth: 0, 85 | clip: 20, 86 | }, 87 | ], 88 | }; 89 | 90 | return ( 91 |
    92 |
    93 |

    Sales Over Time (all stores)

    94 |
    95 | {/* Chart built with Chart.js 3 */} 96 | {/* Change the height attribute to adjust the chart height */} 97 | 98 |
    99 | ); 100 | } 101 | 102 | export default DashboardCard08; 103 | -------------------------------------------------------------------------------- /src/css/additional-styles/utility-patterns.css: -------------------------------------------------------------------------------- 1 | /* Typography */ 2 | .h1 { 3 | @apply text-4xl font-extrabold tracking-tighter; 4 | } 5 | 6 | .h2 { 7 | @apply text-3xl font-extrabold tracking-tighter; 8 | } 9 | 10 | .h3 { 11 | @apply text-3xl font-extrabold; 12 | } 13 | 14 | .h4 { 15 | @apply text-2xl font-extrabold tracking-tight; 16 | } 17 | 18 | @screen md { 19 | .h1 { 20 | @apply text-5xl; 21 | } 22 | 23 | .h2 { 24 | @apply text-4xl; 25 | } 26 | } 27 | 28 | /* Buttons */ 29 | .btn, 30 | .btn-lg, 31 | .btn-sm, 32 | .btn-xs { 33 | @apply font-medium text-sm inline-flex items-center justify-center border border-transparent rounded leading-5 shadow-sm transition duration-150 ease-in-out; 34 | } 35 | 36 | .btn { 37 | @apply px-3 py-2; 38 | } 39 | 40 | .btn-lg { 41 | @apply px-4 py-3; 42 | } 43 | 44 | .btn-sm { 45 | @apply px-2 py-1; 46 | } 47 | 48 | .btn-xs { 49 | @apply px-2 py-0.5; 50 | } 51 | 52 | /* Forms */ 53 | input[type="search"]::-webkit-search-decoration, 54 | input[type="search"]::-webkit-search-cancel-button, 55 | input[type="search"]::-webkit-search-results-button, 56 | input[type="search"]::-webkit-search-results-decoration { 57 | -webkit-appearance: none; 58 | } 59 | 60 | .form-input, 61 | .form-textarea, 62 | .form-multiselect, 63 | .form-select, 64 | .form-checkbox, 65 | .form-radio { 66 | @apply bg-white dark:bg-slate-900/30 border focus:ring-0 focus:ring-offset-0 dark:disabled:bg-slate-700/30 dark:disabled:border-slate-700 dark:disabled:hover:border-slate-700; 67 | } 68 | 69 | .form-input, 70 | .form-textarea, 71 | .form-multiselect, 72 | .form-select, 73 | .form-checkbox { 74 | @apply rounded; 75 | } 76 | 77 | .form-input, 78 | .form-textarea, 79 | .form-multiselect, 80 | .form-select { 81 | @apply text-sm text-slate-800 dark:text-slate-100 leading-5 py-2 px-3 border-slate-200 hover:border-slate-300 focus:border-slate-300 dark:border-slate-700 dark:hover:border-slate-600 dark:focus:border-slate-600 shadow-sm; 82 | } 83 | 84 | .form-input, 85 | .form-textarea { 86 | @apply placeholder-slate-400 dark:placeholder-slate-500; 87 | } 88 | 89 | .form-select { 90 | @apply pr-10; 91 | } 92 | 93 | .form-checkbox, 94 | .form-radio { 95 | @apply text-indigo-500 checked:bg-indigo-500 dark:checked:border-transparent border border-slate-300 focus:border-indigo-300 dark:border-slate-700 dark:focus:border-indigo-500/50; 96 | } 97 | 98 | /* Switch element */ 99 | .form-switch { 100 | @apply relative select-none; 101 | width: 44px; 102 | } 103 | 104 | .form-switch label { 105 | @apply block overflow-hidden cursor-pointer h-6 rounded-full; 106 | } 107 | 108 | .form-switch label > span:first-child { 109 | @apply absolute block rounded-full; 110 | width: 20px; 111 | height: 20px; 112 | top: 2px; 113 | left: 2px; 114 | right: 50%; 115 | transition: all .15s ease-out; 116 | } 117 | 118 | .form-switch input[type="checkbox"]:checked + label { 119 | @apply bg-indigo-500; 120 | } 121 | 122 | .form-switch input[type="checkbox"]:checked + label > span:first-child { 123 | left: 22px; 124 | } 125 | 126 | .form-switch input[type="checkbox"]:disabled + label { 127 | @apply cursor-not-allowed bg-slate-100 dark:bg-slate-700/20 border border-slate-200 dark:border-slate-700; 128 | } 129 | 130 | .form-switch input[type="checkbox"]:disabled + label > span:first-child { 131 | @apply bg-slate-400 dark:bg-slate-600; 132 | } 133 | 134 | /* Chrome, Safari and Opera */ 135 | .no-scrollbar::-webkit-scrollbar { 136 | display: none; 137 | } 138 | 139 | .no-scrollbar { 140 | -ms-overflow-style: none; /* IE and Edge */ 141 | scrollbar-width: none; /* Firefox */ 142 | } -------------------------------------------------------------------------------- /src/utils/Transition.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect, useContext } from 'react'; 2 | import { CSSTransition as ReactCSSTransition } from 'react-transition-group'; 3 | 4 | const TransitionContext = React.createContext({ 5 | parent: {}, 6 | }) 7 | 8 | function useIsInitialRender() { 9 | const isInitialRender = useRef(true); 10 | useEffect(() => { 11 | isInitialRender.current = false; 12 | }, []) 13 | return isInitialRender.current; 14 | } 15 | 16 | function CSSTransition({ 17 | show, 18 | enter = '', 19 | enterStart = '', 20 | enterEnd = '', 21 | leave = '', 22 | leaveStart = '', 23 | leaveEnd = '', 24 | appear, 25 | unmountOnExit, 26 | tag = 'div', 27 | children, 28 | ...rest 29 | }) { 30 | const enterClasses = enter.split(' ').filter((s) => s.length); 31 | const enterStartClasses = enterStart.split(' ').filter((s) => s.length); 32 | const enterEndClasses = enterEnd.split(' ').filter((s) => s.length); 33 | const leaveClasses = leave.split(' ').filter((s) => s.length); 34 | const leaveStartClasses = leaveStart.split(' ').filter((s) => s.length); 35 | const leaveEndClasses = leaveEnd.split(' ').filter((s) => s.length); 36 | const removeFromDom = unmountOnExit; 37 | 38 | function addClasses(node, classes) { 39 | classes.length && node.classList.add(...classes); 40 | } 41 | 42 | function removeClasses(node, classes) { 43 | classes.length && node.classList.remove(...classes); 44 | } 45 | 46 | const nodeRef = React.useRef(null); 47 | const Component = tag; 48 | 49 | return ( 50 | { 56 | nodeRef.current.addEventListener('transitionend', done, false) 57 | }} 58 | onEnter={() => { 59 | if (!removeFromDom) nodeRef.current.style.display = null; 60 | addClasses(nodeRef.current, [...enterClasses, ...enterStartClasses]) 61 | }} 62 | onEntering={() => { 63 | removeClasses(nodeRef.current, enterStartClasses) 64 | addClasses(nodeRef.current, enterEndClasses) 65 | }} 66 | onEntered={() => { 67 | removeClasses(nodeRef.current, [...enterEndClasses, ...enterClasses]) 68 | }} 69 | onExit={() => { 70 | addClasses(nodeRef.current, [...leaveClasses, ...leaveStartClasses]) 71 | }} 72 | onExiting={() => { 73 | removeClasses(nodeRef.current, leaveStartClasses) 74 | addClasses(nodeRef.current, leaveEndClasses) 75 | }} 76 | onExited={() => { 77 | removeClasses(nodeRef.current, [...leaveEndClasses, ...leaveClasses]) 78 | if (!removeFromDom) nodeRef.current.style.display = 'none'; 79 | }} 80 | > 81 | {children} 82 | 83 | ) 84 | } 85 | 86 | function Transition({ show, appear, ...rest }) { 87 | const { parent } = useContext(TransitionContext); 88 | const isInitialRender = useIsInitialRender(); 89 | const isChild = show === undefined; 90 | 91 | if (isChild) { 92 | return ( 93 | 98 | ) 99 | } 100 | 101 | return ( 102 | 111 | 112 | 113 | ) 114 | } 115 | 116 | export default Transition; -------------------------------------------------------------------------------- /src/partials/dashboard/WelcomeBanner.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | 3 | function WelcomeBanner() { 4 | const [greet, setGreet] = useState(""); 5 | useEffect(() => { 6 | greetUser(); 7 | }, []); 8 | 9 | function greetUser() { 10 | const date = new Date(); 11 | const hours = date.getHours(); 12 | const minutes = date.getMinutes(); 13 | if (hours >= 0 && hours < 12) { 14 | setGreet("Good Morning"); 15 | console.log("Morning"); 16 | } else if (hours >= 12 && hours <= 16) { 17 | setGreet("Good Afternoon"); 18 | console.log("Afternoon"); 19 | } else { 20 | setGreet("Good Evening"); 21 | console.log("Evening"); 22 | } 23 | console.log(hours, minutes); 24 | } 25 | return ( 26 |
    27 | {/* Background illustration */} 28 | 89 | 90 | {/* Content */} 91 |
    92 |

    93 | {greet}, John Doe. 👋 94 |

    95 |

    96 | Here is what’s happening with your projects today: 97 |

    98 |
    99 |
    100 | ); 101 | } 102 | 103 | export default WelcomeBanner; 104 | -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard05.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import Tooltip from '../../components/Tooltip'; 3 | import RealtimeChart from '../../charts/RealtimeChart'; 4 | 5 | // Import utilities 6 | import { tailwindConfig, hexToRGB } from '../../utils/Utils'; 7 | 8 | function DashboardCard05() { 9 | 10 | // IMPORTANT: 11 | // Code below is for demo purpose only, and it's not covered by support. 12 | // If you need to replace dummy data with real data, 13 | // refer to Chart.js documentation: https://www.chartjs.org/docs/latest 14 | 15 | // Fake real-time data 16 | const [counter, setCounter] = useState(0); 17 | const [increment, setIncrement] = useState(0); 18 | const [range, setRange] = useState(35); 19 | 20 | // Dummy data to be looped 21 | const data = [ 22 | 57.81, 57.75, 55.48, 54.28, 53.14, 52.25, 51.04, 52.49, 55.49, 56.87, 23 | 53.73, 56.42, 58.06, 55.62, 58.16, 55.22, 58.67, 60.18, 61.31, 63.25, 24 | 65.91, 64.44, 65.97, 62.27, 60.96, 59.34, 55.07, 59.85, 53.79, 51.92, 25 | 50.95, 49.65, 48.09, 49.81, 47.85, 49.52, 50.21, 52.22, 54.42, 53.42, 26 | 50.91, 58.52, 53.37, 57.58, 59.09, 59.36, 58.71, 59.42, 55.93, 57.71, 27 | 50.62, 56.28, 57.37, 53.08, 55.94, 55.82, 53.94, 52.65, 50.25, 28 | ]; 29 | 30 | const [slicedData, setSlicedData] = useState(data.slice(0, range)); 31 | 32 | // Generate fake dates from now to back in time 33 | const generateDates = () => { 34 | const now = new Date(); 35 | const dates = []; 36 | data.forEach((v, i) => { 37 | dates.push(new Date(now - 2000 - i * 2000)); 38 | }); 39 | return dates; 40 | }; 41 | 42 | const [slicedLabels, setSlicedLabels] = useState(generateDates().slice(0, range).reverse()); 43 | 44 | // Fake update every 2 seconds 45 | useEffect(() => { 46 | const interval = setInterval(() => { 47 | setCounter(counter + 1); 48 | }, 2000); 49 | return () => clearInterval(interval) 50 | }, [counter]); 51 | 52 | // Loop through data array and update 53 | useEffect(() => { 54 | setIncrement(increment + 1); 55 | if (increment + range < data.length) { 56 | setSlicedData(([x, ...slicedData]) => [...slicedData, data[increment + range]]); 57 | } else { 58 | setIncrement(0); 59 | setRange(0); 60 | } 61 | setSlicedLabels(([x, ...slicedLabels]) => [...slicedLabels, new Date()]); 62 | return () => setIncrement(0) 63 | // eslint-disable-next-line react-hooks/exhaustive-deps 64 | }, [counter]); 65 | 66 | const chartData = { 67 | labels: slicedLabels, 68 | datasets: [ 69 | // Indigo line 70 | { 71 | data: slicedData, 72 | fill: true, 73 | backgroundColor: `rgba(${hexToRGB(tailwindConfig().theme.colors.blue[500])}, 0.08)`, 74 | borderColor: tailwindConfig().theme.colors.indigo[500], 75 | borderWidth: 2, 76 | tension: 0, 77 | pointRadius: 0, 78 | pointHoverRadius: 3, 79 | pointBackgroundColor: tailwindConfig().theme.colors.indigo[500], 80 | pointHoverBackgroundColor: tailwindConfig().theme.colors.indigo[500], 81 | pointBorderWidth: 0, 82 | pointHoverBorderWidth: 0, 83 | clip: 20, 84 | }, 85 | ], 86 | }; 87 | 88 | return ( 89 |
    90 |
    91 |

    Real Time Value

    92 | 93 |
    Built with Chart.js
    94 |
    95 |
    96 | {/* Chart built with Chart.js 3 */} 97 | {/* Change the height attribute to adjust the chart height */} 98 | 99 |
    100 | ); 101 | } 102 | 103 | export default DashboardCard05; 104 | -------------------------------------------------------------------------------- /src/components/DropdownProfile.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import Transition from "../utils/Transition"; 4 | 5 | import UserAvatar from "../images/user-avatar-32.png"; 6 | 7 | function DropdownProfile({ align }) { 8 | const [dropdownOpen, setDropdownOpen] = useState(false); 9 | 10 | const trigger = useRef(null); 11 | const dropdown = useRef(null); 12 | 13 | // close on click outside 14 | useEffect(() => { 15 | const clickHandler = ({ target }) => { 16 | if (!dropdown.current) return; 17 | if ( 18 | !dropdownOpen || 19 | dropdown.current.contains(target) || 20 | trigger.current.contains(target) 21 | ) 22 | return; 23 | setDropdownOpen(false); 24 | }; 25 | document.addEventListener("click", clickHandler); 26 | return () => document.removeEventListener("click", clickHandler); 27 | }); 28 | 29 | // close if the esc key is pressed 30 | useEffect(() => { 31 | const keyHandler = ({ keyCode }) => { 32 | if (!dropdownOpen || keyCode !== 27) return; 33 | setDropdownOpen(false); 34 | }; 35 | document.addEventListener("keydown", keyHandler); 36 | return () => document.removeEventListener("keydown", keyHandler); 37 | }); 38 | 39 | return ( 40 |
    41 | 67 | 68 | 80 |
    setDropdownOpen(true)} 83 | onBlur={() => setDropdownOpen(false)} 84 | > 85 |
    86 |
    87 | Dan-Codes Inc. 88 |
    89 |
    90 | Administrator 91 |
    92 |
    93 |
      94 |
    • 95 | setDropdownOpen(!dropdownOpen)} 99 | > 100 | Settings 101 | 102 |
    • 103 |
    • 104 | setDropdownOpen(!dropdownOpen)} 108 | > 109 | Sign Out 110 | 111 |
    • 112 |
    113 |
    114 |
    115 |
    116 | ); 117 | } 118 | 119 | export default DropdownProfile; 120 | -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard10.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Image01 from '../../images/user-36-05.jpg'; 4 | import Image02 from '../../images/user-36-06.jpg'; 5 | import Image03 from '../../images/user-36-07.jpg'; 6 | import Image04 from '../../images/user-36-08.jpg'; 7 | import Image05 from '../../images/user-36-09.jpg'; 8 | 9 | function DashboardCard10() { 10 | 11 | const customers = [ 12 | { 13 | id: '0', 14 | image: Image01, 15 | name: 'Alex Shatov', 16 | email: 'alexshatov@gmail.com', 17 | location: '🇺🇸', 18 | spent: '$2,890.66', 19 | }, 20 | { 21 | id: '1', 22 | image: Image02, 23 | name: 'Philip Harbach', 24 | email: 'philip.h@gmail.com', 25 | location: '🇩🇪', 26 | spent: '$2,767.04', 27 | }, 28 | { 29 | id: '2', 30 | image: Image03, 31 | name: 'Mirko Fisuk', 32 | email: 'mirkofisuk@gmail.com', 33 | location: '🇫🇷', 34 | spent: '$2,996.00', 35 | }, 36 | { 37 | id: '3', 38 | image: Image04, 39 | name: 'Olga Semklo', 40 | email: 'olga.s@cool.design', 41 | location: '🇮🇹', 42 | spent: '$1,220.66', 43 | }, 44 | { 45 | id: '4', 46 | image: Image05, 47 | name: 'Burak Long', 48 | email: 'longburak@gmail.com', 49 | location: '🇬🇧', 50 | spent: '$1,890.66', 51 | }, 52 | ]; 53 | 54 | return ( 55 |
    56 |
    57 |

    Customers

    58 |
    59 |
    60 | 61 | {/* Table */} 62 |
    63 | 64 | {/* Table header */} 65 | 66 | 67 | 70 | 73 | 76 | 79 | 80 | 81 | {/* Table body */} 82 | 83 | { 84 | customers.map(customer => { 85 | return ( 86 | 87 | 95 | 98 | 101 | 104 | 105 | ) 106 | }) 107 | } 108 | 109 |
    68 |
    Name
    69 |
    71 |
    Email
    72 |
    74 |
    Spent
    75 |
    77 |
    Country
    78 |
    88 |
    89 |
    90 | {customer.name} 91 |
    92 |
    {customer.name}
    93 |
    94 |
    96 |
    {customer.email}
    97 |
    99 |
    {customer.spent}
    100 |
    102 |
    {customer.location}
    103 |
    110 | 111 |
    112 | 113 |
    114 |
    115 | ); 116 | } 117 | 118 | export default DashboardCard10; 119 | -------------------------------------------------------------------------------- /src/components/DateSelect.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from 'react'; 2 | import Transition from '../utils/Transition'; 3 | 4 | function DateSelect() { 5 | 6 | const options = [ 7 | { 8 | id: 0, 9 | period: 'Today' 10 | }, 11 | { 12 | id: 1, 13 | period: 'Last 7 Days' 14 | }, 15 | { 16 | id: 2, 17 | period: 'Last Month' 18 | }, 19 | { 20 | id: 3, 21 | period: 'Last 12 Months' 22 | }, 23 | { 24 | id: 4, 25 | period: 'All Time' 26 | } 27 | ]; 28 | 29 | const [dropdownOpen, setDropdownOpen] = useState(false); 30 | const [selected, setSelected] = useState(2); 31 | 32 | const trigger = useRef(null); 33 | const dropdown = useRef(null); 34 | 35 | // close on click outside 36 | useEffect(() => { 37 | const clickHandler = ({ target }) => { 38 | if (!dropdown.current) return; 39 | if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return; 40 | setDropdownOpen(false); 41 | }; 42 | document.addEventListener('click', clickHandler); 43 | return () => document.removeEventListener('click', clickHandler); 44 | }); 45 | 46 | // close if the esc key is pressed 47 | useEffect(() => { 48 | const keyHandler = ({ keyCode }) => { 49 | if (!dropdownOpen || keyCode !== 27) return; 50 | setDropdownOpen(false); 51 | }; 52 | document.addEventListener('keydown', keyHandler); 53 | return () => document.removeEventListener('keydown', keyHandler); 54 | }); 55 | 56 | return ( 57 |
    58 | 76 | 87 |
    setDropdownOpen(true)} 91 | onBlur={() => setDropdownOpen(false)} 92 | > 93 | { 94 | options.map(option => { 95 | return ( 96 | 107 | ) 108 | }) 109 | } 110 |
    111 |
    112 |
    113 | ); 114 | } 115 | 116 | export default DateSelect; 117 | -------------------------------------------------------------------------------- /src/pages/Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | import Sidebar from "../partials/Sidebar"; 4 | import Header from "../partials/Header"; 5 | import WelcomeBanner from "../partials/dashboard/WelcomeBanner"; 6 | import DashboardAvatars from "../partials/dashboard/DashboardAvatars"; 7 | import FilterButton from "../components/DropdownFilter"; 8 | import Datepicker from "../components/Datepicker"; 9 | import DashboardCard01 from "../partials/dashboard/DashboardCard01"; 10 | import DashboardCard02 from "../partials/dashboard/DashboardCard02"; 11 | import DashboardCard03 from "../partials/dashboard/DashboardCard03"; 12 | import DashboardCard04 from "../partials/dashboard/DashboardCard04"; 13 | import DashboardCard05 from "../partials/dashboard/DashboardCard05"; 14 | import DashboardCard06 from "../partials/dashboard/DashboardCard06"; 15 | import DashboardCard07 from "../partials/dashboard/DashboardCard07"; 16 | import DashboardCard08 from "../partials/dashboard/DashboardCard08"; 17 | import DashboardCard09 from "../partials/dashboard/DashboardCard09"; 18 | import DashboardCard10 from "../partials/dashboard/DashboardCard10"; 19 | import DashboardCard11 from "../partials/dashboard/DashboardCard11"; 20 | import DashboardCard12 from "../partials/dashboard/DashboardCard12"; 21 | import DashboardCard13 from "../partials/dashboard/DashboardCard13"; 22 | 23 | function Dashboard() { 24 | const [sidebarOpen, setSidebarOpen] = useState(false); 25 | 26 | return ( 27 |
    28 | {/* Sidebar */} 29 | 30 | 31 | {/* Content area */} 32 |
    33 | {/* Site header */} 34 |
    35 | 36 |
    37 |
    38 | {/* Welcome banner */} 39 | 40 | 41 | {/* Dashboard actions */} 42 |
    43 | {/* Left: Avatars */} 44 | 45 | 46 | {/* Right: Actions */} 47 |
    48 | {/* Filter button */} 49 | 50 | {/* Datepicker built with flatpickr */} 51 | 52 | {/* Add view button */} 53 | 62 |
    63 |
    64 | 65 | {/* Cards */} 66 |
    67 | 68 | {/* Line chart (Dan-Codes Advanced) */} 69 | 70 | {/* Line chart (Dan-Codes Professional) */} 71 | 72 | {/* Bar chart (Direct vs Indirect) */} 73 | 74 | {/* Line chart (Real Time Value) */} 75 | 76 | {/* Doughnut chart (Top Countries) */} 77 | 78 | {/* Table (Top Channels) */} 79 | 80 | {/* Line chart (Sales Over Time) */} 81 | 82 | {/* Stacked bar chart (Sales VS Refunds) */} 83 | 84 | {/* Card (Customers) */} 85 | 86 | {/* Card (Reasons for Refunds) */} 87 | 88 | {/* Card (Recent Activity) */} 89 | 90 | {/* Card (Income/Expenses) */} 91 | 92 |
    93 |
    94 |
    95 |
    96 |
    97 | ); 98 | } 99 | 100 | export default Dashboard; 101 | -------------------------------------------------------------------------------- /src/charts/BarChart02.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect, useState } from 'react'; 2 | import { useThemeProvider } from '../utils/ThemeContext'; 3 | 4 | import { chartColors } from './ChartjsConfig'; 5 | import { 6 | Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend, 7 | } from 'chart.js'; 8 | import 'chartjs-adapter-moment'; 9 | 10 | // Import utilities 11 | import { formatValue } from '../utils/Utils'; 12 | 13 | Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend); 14 | 15 | function BarChart02({ 16 | data, 17 | width, 18 | height 19 | }) { 20 | 21 | const [chart, setChart] = useState(null) 22 | const canvas = useRef(null); 23 | const { currentTheme } = useThemeProvider(); 24 | const darkMode = currentTheme === 'dark'; 25 | const { textColor, gridColor, tooltipBodyColor, tooltipBgColor, tooltipBorderColor } = chartColors; 26 | 27 | useEffect(() => { 28 | const ctx = canvas.current; 29 | // eslint-disable-next-line no-unused-vars 30 | const newChart = new Chart(ctx, { 31 | type: 'bar', 32 | data: data, 33 | options: { 34 | layout: { 35 | padding: { 36 | top: 12, 37 | bottom: 16, 38 | left: 20, 39 | right: 20, 40 | }, 41 | }, 42 | scales: { 43 | y: { 44 | stacked: true, 45 | border: { 46 | display: false, 47 | }, 48 | beginAtZero: true, 49 | ticks: { 50 | maxTicksLimit: 5, 51 | callback: (value) => formatValue(value), 52 | color: darkMode ? textColor.dark : textColor.light, 53 | }, 54 | grid: { 55 | color: darkMode ? gridColor.dark : gridColor.light, 56 | }, 57 | }, 58 | x: { 59 | stacked: true, 60 | type: 'time', 61 | time: { 62 | parser: 'MM-DD-YYYY', 63 | unit: 'month', 64 | displayFormats: { 65 | month: 'MMM YY', 66 | }, 67 | }, 68 | border: { 69 | display: false, 70 | }, 71 | grid: { 72 | display: false, 73 | }, 74 | ticks: { 75 | autoSkipPadding: 48, 76 | maxRotation: 0, 77 | color: darkMode ? textColor.dark : textColor.light, 78 | }, 79 | }, 80 | }, 81 | plugins: { 82 | legend: { 83 | display: false, 84 | }, 85 | tooltip: { 86 | callbacks: { 87 | title: () => false, // Disable tooltip title 88 | label: (context) => formatValue(context.parsed.y), 89 | }, 90 | bodyColor: darkMode ? tooltipBodyColor.dark : tooltipBodyColor.light, 91 | backgroundColor: darkMode ? tooltipBgColor.dark : tooltipBgColor.light, 92 | borderColor: darkMode ? tooltipBorderColor.dark : tooltipBorderColor.light, 93 | }, 94 | }, 95 | interaction: { 96 | intersect: false, 97 | mode: 'nearest', 98 | }, 99 | animation: { 100 | duration: 200, 101 | }, 102 | maintainAspectRatio: false, 103 | resizeDelay: 200, 104 | }, 105 | }); 106 | setChart(newChart); 107 | return () => newChart.destroy(); 108 | // eslint-disable-next-line react-hooks/exhaustive-deps 109 | }, []); 110 | 111 | useEffect(() => { 112 | if (!chart) return; 113 | 114 | if (darkMode) { 115 | chart.options.scales.x.ticks.color = textColor.dark; 116 | chart.options.scales.y.ticks.color = textColor.dark; 117 | chart.options.scales.y.grid.color = gridColor.dark; 118 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.dark; 119 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.dark; 120 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.dark; 121 | } else { 122 | chart.options.scales.x.ticks.color = textColor.light; 123 | chart.options.scales.y.ticks.color = textColor.light; 124 | chart.options.scales.y.grid.color = gridColor.light; 125 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.light; 126 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.light; 127 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.light; 128 | } 129 | chart.update('none'); 130 | }, [currentTheme]); 131 | 132 | return ( 133 | 134 | ); 135 | } 136 | 137 | export default BarChart02; -------------------------------------------------------------------------------- /src/components/DropdownHelp.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import Transition from '../utils/Transition'; 4 | 5 | function DropdownHelp({ 6 | align 7 | }) { 8 | 9 | const [dropdownOpen, setDropdownOpen] = useState(false); 10 | 11 | const trigger = useRef(null); 12 | const dropdown = useRef(null); 13 | 14 | // close on click outside 15 | useEffect(() => { 16 | const clickHandler = ({ target }) => { 17 | if (!dropdown.current) return; 18 | if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return; 19 | setDropdownOpen(false); 20 | }; 21 | document.addEventListener('click', clickHandler); 22 | return () => document.removeEventListener('click', clickHandler); 23 | }); 24 | 25 | // close if the esc key is pressed 26 | useEffect(() => { 27 | const keyHandler = ({ keyCode }) => { 28 | if (!dropdownOpen || keyCode !== 27) return; 29 | setDropdownOpen(false); 30 | }; 31 | document.addEventListener('keydown', keyHandler); 32 | return () => document.removeEventListener('keydown', keyHandler); 33 | }); 34 | 35 | return ( 36 |
    37 | 49 | 50 | 60 |
    setDropdownOpen(true)} 63 | onBlur={() => setDropdownOpen(false)} 64 | > 65 |
    Need help?
    66 |
      67 |
    • 68 | setDropdownOpen(!dropdownOpen)} 72 | > 73 | 74 | 75 | 76 | 77 | Documentation 78 | 79 |
    • 80 |
    • 81 | setDropdownOpen(!dropdownOpen)} 85 | > 86 | 87 | 88 | 89 | Support Site 90 | 91 |
    • 92 |
    • 93 | setDropdownOpen(!dropdownOpen)} 97 | > 98 | 99 | 100 | 101 | Contact us 102 | 103 |
    • 104 |
    105 |
    106 |
    107 |
    108 | ) 109 | } 110 | 111 | export default DropdownHelp; -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard01.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import LineChart from "../../charts/LineChart01"; 4 | import Icon from "../../images/icon-01.svg"; 5 | import EditMenu from "../../components/DropdownEditMenu"; 6 | 7 | // Import utilities 8 | import { tailwindConfig, hexToRGB } from "../../utils/Utils"; 9 | 10 | function DashboardCard01() { 11 | const chartData = { 12 | labels: [ 13 | "12-01-2020", 14 | "01-01-2021", 15 | "02-01-2021", 16 | "03-01-2021", 17 | "04-01-2021", 18 | "05-01-2021", 19 | "06-01-2021", 20 | "07-01-2021", 21 | "08-01-2021", 22 | "09-01-2021", 23 | "10-01-2021", 24 | "11-01-2021", 25 | "12-01-2021", 26 | "01-01-2022", 27 | "02-01-2022", 28 | "03-01-2022", 29 | "04-01-2022", 30 | "05-01-2022", 31 | "06-01-2022", 32 | "07-01-2022", 33 | "08-01-2022", 34 | "09-01-2022", 35 | "10-01-2022", 36 | "11-01-2022", 37 | "12-01-2022", 38 | "01-01-2023", 39 | ], 40 | datasets: [ 41 | // Indigo line 42 | { 43 | data: [ 44 | 732, 610, 610, 504, 504, 504, 349, 349, 504, 342, 504, 610, 391, 192, 45 | 154, 273, 191, 191, 126, 263, 349, 252, 423, 622, 470, 532, 46 | ], 47 | fill: true, 48 | backgroundColor: `rgba(${hexToRGB( 49 | tailwindConfig().theme.colors.blue[500] 50 | )}, 0.08)`, 51 | borderColor: tailwindConfig().theme.colors.indigo[500], 52 | borderWidth: 2, 53 | tension: 0, 54 | pointRadius: 0, 55 | pointHoverRadius: 3, 56 | pointBackgroundColor: tailwindConfig().theme.colors.indigo[500], 57 | pointHoverBackgroundColor: tailwindConfig().theme.colors.indigo[500], 58 | pointBorderWidth: 0, 59 | pointHoverBorderWidth: 0, 60 | clip: 20, 61 | }, 62 | // Gray line 63 | { 64 | data: [ 65 | 532, 532, 532, 404, 404, 314, 314, 314, 314, 314, 234, 314, 234, 234, 66 | 314, 314, 314, 388, 314, 202, 202, 202, 202, 314, 720, 642, 67 | ], 68 | borderColor: `rgba(${hexToRGB( 69 | tailwindConfig().theme.colors.slate[500] 70 | )}, 0.25)`, 71 | borderWidth: 2, 72 | tension: 0, 73 | pointRadius: 0, 74 | pointHoverRadius: 3, 75 | pointBackgroundColor: `rgba(${hexToRGB( 76 | tailwindConfig().theme.colors.slate[500] 77 | )}, 0.25)`, 78 | pointHoverBackgroundColor: `rgba(${hexToRGB( 79 | tailwindConfig().theme.colors.slate[500] 80 | )}, 0.25)`, 81 | pointBorderWidth: 0, 82 | pointHoverBorderWidth: 0, 83 | clip: 20, 84 | }, 85 | ], 86 | }; 87 | 88 | return ( 89 |
    90 |
    91 |
    92 | {/* Icon */} 93 | Icon 01 94 | {/* Menu button */} 95 | 96 |
  • 97 | 101 | Option 1 102 | 103 |
  • 104 |
  • 105 | 109 | Option 2 110 | 111 |
  • 112 |
  • 113 | 117 | Remove 118 | 119 |
  • 120 |
    121 |
    122 |

    123 | Dan-Codes Plus 124 |

    125 |
    126 | Sales 127 |
    128 |
    129 |
    130 | $24,780 131 |
    132 |
    133 | +49% 134 |
    135 |
    136 |
    137 | {/* Chart built with Chart.js 3 */} 138 |
    139 | {/* Change the height attribute to adjust the chart height */} 140 | 141 |
    142 |
    143 | ); 144 | } 145 | 146 | export default DashboardCard01; 147 | -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard02.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import LineChart from "../../charts/LineChart01"; 4 | import Icon from "../../images/icon-02.svg"; 5 | import EditMenu from "../../components/DropdownEditMenu"; 6 | 7 | // Import utilities 8 | import { tailwindConfig, hexToRGB } from "../../utils/Utils"; 9 | 10 | function DashboardCard02() { 11 | const chartData = { 12 | labels: [ 13 | "12-01-2020", 14 | "01-01-2021", 15 | "02-01-2021", 16 | "03-01-2021", 17 | "04-01-2021", 18 | "05-01-2021", 19 | "06-01-2021", 20 | "07-01-2021", 21 | "08-01-2021", 22 | "09-01-2021", 23 | "10-01-2021", 24 | "11-01-2021", 25 | "12-01-2021", 26 | "01-01-2022", 27 | "02-01-2022", 28 | "03-01-2022", 29 | "04-01-2022", 30 | "05-01-2022", 31 | "06-01-2022", 32 | "07-01-2022", 33 | "08-01-2022", 34 | "09-01-2022", 35 | "10-01-2022", 36 | "11-01-2022", 37 | "12-01-2022", 38 | "01-01-2023", 39 | ], 40 | datasets: [ 41 | // Indigo line 42 | { 43 | data: [ 44 | 622, 622, 426, 471, 365, 365, 238, 324, 288, 206, 324, 324, 500, 409, 45 | 409, 273, 232, 273, 500, 570, 767, 808, 685, 767, 685, 400, 46 | ], 47 | fill: true, 48 | backgroundColor: `rgba(${hexToRGB( 49 | tailwindConfig().theme.colors.blue[500] 50 | )}, 0.08)`, 51 | borderColor: tailwindConfig().theme.colors.indigo[500], 52 | borderWidth: 2, 53 | tension: 0, 54 | pointRadius: 0, 55 | pointHoverRadius: 3, 56 | pointBackgroundColor: tailwindConfig().theme.colors.indigo[500], 57 | pointHoverBackgroundColor: tailwindConfig().theme.colors.indigo[500], 58 | pointBorderWidth: 0, 59 | pointHoverBorderWidth: 0, 60 | clip: 20, 61 | }, 62 | // Gray line 63 | { 64 | data: [ 65 | 732, 610, 610, 504, 504, 504, 349, 349, 504, 342, 504, 610, 391, 192, 66 | 154, 273, 191, 191, 126, 263, 349, 252, 423, 622, 470, 532, 67 | ], 68 | borderColor: `rgba(${hexToRGB( 69 | tailwindConfig().theme.colors.slate[500] 70 | )}, 0.25)`, 71 | borderWidth: 2, 72 | tension: 0, 73 | pointRadius: 0, 74 | pointHoverRadius: 3, 75 | pointBackgroundColor: `rgba(${hexToRGB( 76 | tailwindConfig().theme.colors.slate[500] 77 | )}, 0.25)`, 78 | pointHoverBackgroundColor: `rgba(${hexToRGB( 79 | tailwindConfig().theme.colors.slate[500] 80 | )}, 0.25)`, 81 | pointBorderWidth: 0, 82 | pointHoverBorderWidth: 0, 83 | clip: 20, 84 | }, 85 | ], 86 | }; 87 | 88 | return ( 89 |
    90 |
    91 |
    92 | {/* Icon */} 93 | Icon 02 94 | {/* Menu button */} 95 | 96 |
  • 97 | 101 | Option 1 102 | 103 |
  • 104 |
  • 105 | 109 | Option 2 110 | 111 |
  • 112 |
  • 113 | 117 | Remove 118 | 119 |
  • 120 |
    121 |
    122 |

    123 | Dan-Codes Advanced 124 |

    125 |
    126 | Sales 127 |
    128 |
    129 |
    130 | $17,489 131 |
    132 |
    133 | -14% 134 |
    135 |
    136 |
    137 | {/* Chart built with Chart.js 3 */} 138 |
    139 | {/* Change the height attribute to adjust the chart height */} 140 | 141 |
    142 |
    143 | ); 144 | } 145 | 146 | export default DashboardCard02; 147 | -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard03.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import LineChart from "../../charts/LineChart01"; 4 | import Icon from "../../images/icon-03.svg"; 5 | import EditMenu from "../../components/DropdownEditMenu"; 6 | 7 | // Import utilities 8 | import { tailwindConfig, hexToRGB } from "../../utils/Utils"; 9 | 10 | function DashboardCard03() { 11 | const chartData = { 12 | labels: [ 13 | "12-01-2020", 14 | "01-01-2021", 15 | "02-01-2021", 16 | "03-01-2021", 17 | "04-01-2021", 18 | "05-01-2021", 19 | "06-01-2021", 20 | "07-01-2021", 21 | "08-01-2021", 22 | "09-01-2021", 23 | "10-01-2021", 24 | "11-01-2021", 25 | "12-01-2021", 26 | "01-01-2022", 27 | "02-01-2022", 28 | "03-01-2022", 29 | "04-01-2022", 30 | "05-01-2022", 31 | "06-01-2022", 32 | "07-01-2022", 33 | "08-01-2022", 34 | "09-01-2022", 35 | "10-01-2022", 36 | "11-01-2022", 37 | "12-01-2022", 38 | "01-01-2023", 39 | ], 40 | datasets: [ 41 | // Indigo line 42 | { 43 | data: [ 44 | 540, 466, 540, 466, 385, 432, 334, 334, 289, 289, 200, 289, 222, 289, 45 | 289, 403, 554, 304, 289, 270, 134, 270, 829, 344, 388, 364, 46 | ], 47 | fill: true, 48 | backgroundColor: `rgba(${hexToRGB( 49 | tailwindConfig().theme.colors.blue[500] 50 | )}, 0.08)`, 51 | borderColor: tailwindConfig().theme.colors.indigo[500], 52 | borderWidth: 2, 53 | tension: 0, 54 | pointRadius: 0, 55 | pointHoverRadius: 3, 56 | pointBackgroundColor: tailwindConfig().theme.colors.indigo[500], 57 | pointHoverBackgroundColor: tailwindConfig().theme.colors.indigo[500], 58 | pointBorderWidth: 0, 59 | pointHoverBorderWidth: 0, 60 | clip: 20, 61 | }, 62 | // Gray line 63 | { 64 | data: [ 65 | 689, 562, 477, 477, 477, 477, 458, 314, 430, 378, 430, 498, 642, 350, 66 | 145, 145, 354, 260, 188, 188, 300, 300, 282, 364, 660, 554, 67 | ], 68 | borderColor: `rgba(${hexToRGB( 69 | tailwindConfig().theme.colors.slate[500] 70 | )}, 0.25)`, 71 | borderWidth: 2, 72 | tension: 0, 73 | pointRadius: 0, 74 | pointHoverRadius: 3, 75 | pointBackgroundColor: `rgba(${hexToRGB( 76 | tailwindConfig().theme.colors.slate[500] 77 | )}, 0.25)`, 78 | pointHoverBackgroundColor: `rgba(${hexToRGB( 79 | tailwindConfig().theme.colors.slate[500] 80 | )}, 0.25)`, 81 | pointBorderWidth: 0, 82 | pointHoverBorderWidth: 0, 83 | clip: 20, 84 | }, 85 | ], 86 | }; 87 | 88 | return ( 89 |
    90 |
    91 |
    92 | {/* Icon */} 93 | Icon 03 94 | {/* Menu button */} 95 | 96 |
  • 97 | 101 | Option 1 102 | 103 |
  • 104 |
  • 105 | 109 | Option 2 110 | 111 |
  • 112 |
  • 113 | 117 | Remove 118 | 119 |
  • 120 |
    121 |
    122 |

    123 | Dan-Codes Professional 124 |

    125 |
    126 | Sales 127 |
    128 |
    129 |
    130 | $9,962 131 |
    132 |
    133 | +49% 134 |
    135 |
    136 |
    137 | {/* Chart built with Chart.js 3 */} 138 |
    139 | {/* Change the height attribute to adjust the chart height */} 140 | 141 |
    142 |
    143 | ); 144 | } 145 | 146 | export default DashboardCard03; 147 | -------------------------------------------------------------------------------- /src/charts/DoughnutChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect, useState } from 'react'; 2 | import { useThemeProvider } from '../utils/ThemeContext'; 3 | 4 | import { chartColors } from './ChartjsConfig'; 5 | import { 6 | Chart, DoughnutController, ArcElement, TimeScale, Tooltip, 7 | } from 'chart.js'; 8 | import 'chartjs-adapter-moment'; 9 | 10 | // Import utilities 11 | import { tailwindConfig } from '../utils/Utils'; 12 | 13 | Chart.register(DoughnutController, ArcElement, TimeScale, Tooltip); 14 | 15 | function DoughnutChart({ 16 | data, 17 | width, 18 | height 19 | }) { 20 | 21 | const [chart, setChart] = useState(null) 22 | const canvas = useRef(null); 23 | const legend = useRef(null); 24 | const { currentTheme } = useThemeProvider(); 25 | const darkMode = currentTheme === 'dark'; 26 | const { tooltipTitleColor, tooltipBodyColor, tooltipBgColor, tooltipBorderColor } = chartColors; 27 | 28 | useEffect(() => { 29 | const ctx = canvas.current; 30 | // eslint-disable-next-line no-unused-vars 31 | const newChart = new Chart(ctx, { 32 | type: 'doughnut', 33 | data: data, 34 | options: { 35 | cutout: '80%', 36 | layout: { 37 | padding: 24, 38 | }, 39 | plugins: { 40 | legend: { 41 | display: false, 42 | }, 43 | tooltip: { 44 | titleColor: darkMode ? tooltipTitleColor.dark : tooltipTitleColor.light, 45 | bodyColor: darkMode ? tooltipBodyColor.dark : tooltipBodyColor.light, 46 | backgroundColor: darkMode ? tooltipBgColor.dark : tooltipBgColor.light, 47 | borderColor: darkMode ? tooltipBorderColor.dark : tooltipBorderColor.light, 48 | }, 49 | }, 50 | interaction: { 51 | intersect: false, 52 | mode: 'nearest', 53 | }, 54 | animation: { 55 | duration: 500, 56 | }, 57 | maintainAspectRatio: false, 58 | resizeDelay: 200, 59 | }, 60 | plugins: [ 61 | { 62 | id: 'htmlLegend', 63 | afterUpdate(c, args, options) { 64 | const ul = legend.current; 65 | if (!ul) return; 66 | // Remove old legend items 67 | while (ul.firstChild) { 68 | ul.firstChild.remove(); 69 | } 70 | // Reuse the built-in legendItems generator 71 | const items = c.options.plugins.legend.labels.generateLabels(c); 72 | items.forEach((item) => { 73 | const li = document.createElement('li'); 74 | li.style.margin = tailwindConfig().theme.margin[1]; 75 | // Button element 76 | const button = document.createElement('button'); 77 | button.classList.add('btn-xs', 'bg-white', 'dark:bg-slate-800', 'text-slate-500', 'dark:text-slate-400', 'border', 'border-slate-200', 'dark:border-slate-700', 'shadow-md'); 78 | button.style.opacity = item.hidden ? '.3' : ''; 79 | button.onclick = () => { 80 | c.toggleDataVisibility(item.index); 81 | c.update(); 82 | }; 83 | // Color box 84 | const box = document.createElement('span'); 85 | box.style.display = 'block'; 86 | box.style.width = tailwindConfig().theme.width[2]; 87 | box.style.height = tailwindConfig().theme.height[2]; 88 | box.style.backgroundColor = item.fillStyle; 89 | box.style.borderRadius = tailwindConfig().theme.borderRadius.sm; 90 | box.style.marginRight = tailwindConfig().theme.margin[1]; 91 | box.style.pointerEvents = 'none'; 92 | // Label 93 | const label = document.createElement('span'); 94 | label.style.display = 'flex'; 95 | label.style.alignItems = 'center'; 96 | const labelText = document.createTextNode(item.text); 97 | label.appendChild(labelText); 98 | li.appendChild(button); 99 | button.appendChild(box); 100 | button.appendChild(label); 101 | ul.appendChild(li); 102 | }); 103 | }, 104 | }, 105 | ], 106 | }); 107 | setChart(newChart); 108 | return () => newChart.destroy(); 109 | // eslint-disable-next-line react-hooks/exhaustive-deps 110 | }, []); 111 | 112 | useEffect(() => { 113 | if (!chart) return; 114 | 115 | if (darkMode) { 116 | chart.options.plugins.tooltip.titleColor = tooltipTitleColor.dark; 117 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.dark; 118 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.dark; 119 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.dark; 120 | } else { 121 | chart.options.plugins.tooltip.titleColor = tooltipTitleColor.light; 122 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.light; 123 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.light; 124 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.light; 125 | } 126 | chart.update('none'); 127 | }, [currentTheme]); 128 | 129 | return ( 130 |
    131 |
    132 | 133 |
    134 |
    135 |
      136 |
      137 |
      138 | ); 139 | } 140 | 141 | export default DoughnutChart; -------------------------------------------------------------------------------- /src/components/DropdownNotifications.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import Transition from '../utils/Transition'; 4 | 5 | function DropdownNotifications({ 6 | align 7 | }) { 8 | 9 | const [dropdownOpen, setDropdownOpen] = useState(false); 10 | 11 | const trigger = useRef(null); 12 | const dropdown = useRef(null); 13 | 14 | // close on click outside 15 | useEffect(() => { 16 | const clickHandler = ({ target }) => { 17 | if (!dropdown.current) return; 18 | if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return; 19 | setDropdownOpen(false); 20 | }; 21 | document.addEventListener('click', clickHandler); 22 | return () => document.removeEventListener('click', clickHandler); 23 | }); 24 | 25 | // close if the esc key is pressed 26 | useEffect(() => { 27 | const keyHandler = ({ keyCode }) => { 28 | if (!dropdownOpen || keyCode !== 27) return; 29 | setDropdownOpen(false); 30 | }; 31 | document.addEventListener('keydown', keyHandler); 32 | return () => document.removeEventListener('keydown', keyHandler); 33 | }); 34 | 35 | return ( 36 |
      37 | 51 | 52 | 62 |
      setDropdownOpen(true)} 65 | onBlur={() => setDropdownOpen(false)} 66 | > 67 |
      Notifications
      68 |
        69 |
      • 70 | setDropdownOpen(!dropdownOpen)} 74 | > 75 | 📣 Edit your information in a swipe Sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim. 76 | Feb 12, 2021 77 | 78 |
      • 79 |
      • 80 | setDropdownOpen(!dropdownOpen)} 84 | > 85 | 📣 Edit your information in a swipe Sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim. 86 | Feb 9, 2021 87 | 88 |
      • 89 |
      • 90 | setDropdownOpen(!dropdownOpen)} 94 | > 95 | 🚀Say goodbye to paper receipts! Sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim. 96 | Jan 24, 2020 97 | 98 |
      • 99 |
      100 |
      101 |
      102 |
      103 | ) 104 | } 105 | 106 | export default DropdownNotifications; -------------------------------------------------------------------------------- /src/components/DropdownFilter.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from 'react'; 2 | import Transition from '../utils/Transition'; 3 | 4 | function DropdownFilter({ 5 | align 6 | }) { 7 | 8 | const [dropdownOpen, setDropdownOpen] = useState(false); 9 | 10 | const trigger = useRef(null); 11 | const dropdown = useRef(null); 12 | 13 | // close on click outside 14 | useEffect(() => { 15 | const clickHandler = ({ target }) => { 16 | if (!dropdown.current) return; 17 | if (!dropdownOpen || dropdown.current.contains(target) || trigger.current.contains(target)) return; 18 | setDropdownOpen(false); 19 | }; 20 | document.addEventListener('click', clickHandler); 21 | return () => document.removeEventListener('click', clickHandler); 22 | }); 23 | 24 | // close if the esc key is pressed 25 | useEffect(() => { 26 | const keyHandler = ({ keyCode }) => { 27 | if (!dropdownOpen || keyCode !== 27) return; 28 | setDropdownOpen(false); 29 | }; 30 | document.addEventListener('keydown', keyHandler); 31 | return () => document.removeEventListener('keydown', keyHandler); 32 | }); 33 | 34 | return ( 35 |
      36 | 49 | 62 |
      63 |
      Filters
      64 |
        65 |
      • 66 | 70 |
      • 71 |
      • 72 | 76 |
      • 77 |
      • 78 | 82 |
      • 83 |
      • 84 | 88 |
      • 89 |
      • 90 | 94 |
      • 95 |
      • 96 | 100 |
      • 101 |
      102 |
      103 |
        104 |
      • 105 | 108 |
      • 109 |
      • 110 | 117 |
      • 118 |
      119 |
      120 |
      121 |
      122 |
      123 | ); 124 | } 125 | 126 | export default DropdownFilter; 127 | -------------------------------------------------------------------------------- /src/charts/BarChart03.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect, useState } from 'react'; 2 | import { useThemeProvider } from '../utils/ThemeContext'; 3 | 4 | import { chartColors } from './ChartjsConfig'; 5 | import { 6 | Chart, BarController, BarElement, LinearScale, CategoryScale, Tooltip, Legend, 7 | } from 'chart.js'; 8 | import 'chartjs-adapter-moment'; 9 | 10 | // Import utilities 11 | import { tailwindConfig } from '../utils/Utils'; 12 | 13 | Chart.register(BarController, BarElement, LinearScale, CategoryScale, Tooltip, Legend); 14 | 15 | function BarChart03({ 16 | data, 17 | width, 18 | height 19 | }) { 20 | 21 | const [chart, setChart] = useState(null); 22 | const canvas = useRef(null); 23 | const legend = useRef(null); 24 | const { currentTheme } = useThemeProvider(); 25 | const darkMode = currentTheme === 'dark'; 26 | const { tooltipBodyColor, tooltipBgColor, tooltipBorderColor } = chartColors; 27 | 28 | useEffect(() => { 29 | 30 | // Calculate sum of values 31 | const reducer = (accumulator, currentValue) => accumulator + currentValue; 32 | const values = data.datasets.map(x => x.data.reduce(reducer)); 33 | const max = values.reduce(reducer); 34 | 35 | const ctx = canvas.current; 36 | // eslint-disable-next-line no-unused-vars 37 | const newChart = new Chart(ctx, { 38 | type: 'bar', 39 | data: data, 40 | options: { 41 | indexAxis: 'y', 42 | layout: { 43 | padding: { 44 | top: 12, 45 | bottom: 12, 46 | left: 20, 47 | right: 20, 48 | }, 49 | }, 50 | scales: { 51 | x: { 52 | stacked: true, 53 | display: false, 54 | max: max, 55 | }, 56 | y: { 57 | stacked: true, 58 | display: false, 59 | }, 60 | }, 61 | plugins: { 62 | legend: { 63 | display: false, 64 | }, 65 | tooltip: { 66 | callbacks: { 67 | title: () => false, // Disable tooltip title 68 | label: (context) => context.parsed.x, 69 | }, 70 | bodyColor: darkMode ? tooltipBodyColor.dark : tooltipBodyColor.light, 71 | backgroundColor: darkMode ? tooltipBgColor.dark : tooltipBgColor.light, 72 | borderColor: darkMode ? tooltipBorderColor.dark : tooltipBorderColor.light, 73 | }, 74 | }, 75 | interaction: { 76 | intersect: false, 77 | mode: 'nearest', 78 | }, 79 | animation: { 80 | duration: 500, 81 | }, 82 | maintainAspectRatio: false, 83 | resizeDelay: 200, 84 | }, 85 | plugins: [ 86 | { 87 | id: 'htmlLegend', 88 | afterUpdate(c, args, options) { 89 | const ul = legend.current; 90 | if (!ul) return; 91 | // Remove old legend items 92 | while (ul.firstChild) { 93 | ul.firstChild.remove(); 94 | } 95 | // Reuse the built-in legendItems generator 96 | const items = c.options.plugins.legend.labels.generateLabels(c); 97 | items.forEach((item) => { 98 | const li = document.createElement('li'); 99 | li.style.display = 'flex'; 100 | li.style.justifyContent = 'space-between'; 101 | li.style.alignItems = 'center'; 102 | li.style.paddingTop = tailwindConfig().theme.padding[2.5]; 103 | li.style.paddingBottom = tailwindConfig().theme.padding[2.5]; 104 | const wrapper = document.createElement('div'); 105 | wrapper.style.display = 'flex'; 106 | wrapper.style.alignItems = 'center'; 107 | const box = document.createElement('div'); 108 | box.style.width = tailwindConfig().theme.width[3]; 109 | box.style.height = tailwindConfig().theme.width[3]; 110 | box.style.borderRadius = tailwindConfig().theme.borderRadius.sm; 111 | box.style.marginRight = tailwindConfig().theme.margin[3]; 112 | box.style.backgroundColor = item.fillStyle; 113 | const label = document.createElement('div'); 114 | const value = document.createElement('div'); 115 | value.style.fontWeight = tailwindConfig().theme.fontWeight.medium; 116 | value.style.marginLeft = tailwindConfig().theme.margin[3]; 117 | value.style.color = item.text === 'Other' ? tailwindConfig().theme.colors.slate[400] : item.fillStyle; 118 | const theValue = c.data.datasets[item.datasetIndex].data.reduce((a, b) => a + b, 0); 119 | const valueText = document.createTextNode(`${parseInt((theValue / max) * 100)}%`); 120 | const labelText = document.createTextNode(item.text); 121 | value.appendChild(valueText); 122 | label.appendChild(labelText); 123 | ul.appendChild(li); 124 | li.appendChild(wrapper); 125 | li.appendChild(value); 126 | wrapper.appendChild(box); 127 | wrapper.appendChild(label); 128 | }); 129 | }, 130 | }, 131 | ], 132 | }); 133 | setChart(newChart); 134 | return () => newChart.destroy(); 135 | // eslint-disable-next-line react-hooks/exhaustive-deps 136 | }, []); 137 | 138 | useEffect(() => { 139 | if (!chart) return; 140 | 141 | if (darkMode) { 142 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.dark; 143 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.dark; 144 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.dark; 145 | } else { 146 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.light; 147 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.light; 148 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.light; 149 | } 150 | chart.update('none'); 151 | }, [currentTheme]); 152 | 153 | return ( 154 |
      155 |
      156 | 157 |
      158 |
      159 |
        160 |
          161 |
          162 |
          163 | ); 164 | } 165 | 166 | export default BarChart03; -------------------------------------------------------------------------------- /src/charts/RealtimeChart.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect, useState } from "react"; 2 | import { useThemeProvider } from "../utils/ThemeContext"; 3 | 4 | import { chartColors } from "./ChartjsConfig"; 5 | import { 6 | Chart, 7 | LineController, 8 | LineElement, 9 | Filler, 10 | PointElement, 11 | LinearScale, 12 | TimeScale, 13 | Tooltip, 14 | } from "chart.js"; 15 | import "chartjs-adapter-moment"; 16 | 17 | // Import utilities 18 | import { tailwindConfig, formatValue } from "../utils/Utils"; 19 | 20 | Chart.register( 21 | LineController, 22 | LineElement, 23 | Filler, 24 | PointElement, 25 | LinearScale, 26 | TimeScale, 27 | Tooltip 28 | ); 29 | 30 | function RealtimeChart({ data, width, height }) { 31 | const [chart, setChart] = useState(null); 32 | const canvas = useRef(null); 33 | const chartValue = useRef(null); 34 | const chartDeviation = useRef(null); 35 | const { currentTheme } = useThemeProvider(); 36 | const darkMode = currentTheme === "dark"; 37 | const { 38 | textColor, 39 | gridColor, 40 | tooltipTitleColor, 41 | tooltipBodyColor, 42 | tooltipBgColor, 43 | tooltipBorderColor, 44 | } = chartColors; 45 | 46 | useEffect(() => { 47 | const ctx = canvas.current; 48 | // eslint-disable-next-line no-unused-vars 49 | const newChart = new Chart(ctx, { 50 | type: "line", 51 | data: data, 52 | options: { 53 | layout: { 54 | padding: 20, 55 | }, 56 | scales: { 57 | y: { 58 | border: { 59 | display: false, 60 | }, 61 | suggestedMin: 40, 62 | suggestedMax: 80, 63 | ticks: { 64 | maxTicksLimit: 5, 65 | callback: (value) => formatValue(value), 66 | color: darkMode ? textColor.dark : textColor.light, 67 | }, 68 | grid: { 69 | color: darkMode ? gridColor.dark : gridColor.light, 70 | }, 71 | }, 72 | x: { 73 | type: "time", 74 | time: { 75 | parser: "hh:mm:ss", 76 | unit: "second", 77 | tooltipFormat: "MMM DD, H:mm:ss a", 78 | displayFormats: { 79 | second: "H:mm:ss", 80 | }, 81 | }, 82 | border: { 83 | display: false, 84 | }, 85 | grid: { 86 | display: false, 87 | }, 88 | ticks: { 89 | autoSkipPadding: 48, 90 | maxRotation: 0, 91 | color: darkMode ? textColor.dark : textColor.light, 92 | }, 93 | }, 94 | }, 95 | plugins: { 96 | legend: { 97 | display: false, 98 | }, 99 | tooltip: { 100 | titleFont: { 101 | weight: "600", 102 | }, 103 | callbacks: { 104 | label: (context) => formatValue(context.parsed.y), 105 | }, 106 | titleColor: darkMode 107 | ? tooltipTitleColor.dark 108 | : tooltipTitleColor.light, 109 | bodyColor: darkMode 110 | ? tooltipBodyColor.dark 111 | : tooltipBodyColor.light, 112 | backgroundColor: darkMode 113 | ? tooltipBgColor.dark 114 | : tooltipBgColor.light, 115 | borderColor: darkMode 116 | ? tooltipBorderColor.dark 117 | : tooltipBorderColor.light, 118 | }, 119 | }, 120 | interaction: { 121 | intersect: false, 122 | mode: "nearest", 123 | }, 124 | animation: false, 125 | maintainAspectRatio: false, 126 | }, 127 | }); 128 | setChart(newChart); 129 | return () => newChart.destroy(); 130 | // eslint-disable-next-line react-hooks/exhaustive-deps 131 | }, [data]); 132 | 133 | // Update header values 134 | useEffect(() => { 135 | const currentValue = 136 | data.datasets[0].data[data.datasets[0].data.length - 1]; 137 | const previousValue = 138 | data.datasets[0].data[data.datasets[0].data.length - 2]; 139 | const diff = ((currentValue - previousValue) / previousValue) * 100; 140 | chartValue.current.innerHTML = 141 | data.datasets[0].data[data.datasets[0].data.length - 1]; 142 | if (diff < 0) { 143 | chartDeviation.current.style.backgroundColor = 144 | tailwindConfig().theme.colors.amber[500]; 145 | } else { 146 | chartDeviation.current.style.backgroundColor = 147 | tailwindConfig().theme.colors.emerald[500]; 148 | } 149 | chartDeviation.current.innerHTML = `${diff > 0 ? "+" : ""}${diff.toFixed( 150 | 2 151 | )}%`; 152 | }, [data]); 153 | 154 | useEffect(() => { 155 | if (!chart) return; 156 | 157 | if (darkMode) { 158 | chart.options.scales.x.ticks.color = textColor.dark; 159 | chart.options.scales.y.ticks.color = textColor.dark; 160 | chart.options.scales.y.grid.color = gridColor.dark; 161 | chart.options.plugins.tooltip.titleColor = tooltipTitleColor.dark; 162 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.dark; 163 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.dark; 164 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.dark; 165 | } else { 166 | chart.options.scales.x.ticks.color = textColor.light; 167 | chart.options.scales.y.ticks.color = textColor.light; 168 | chart.options.scales.y.grid.color = gridColor.light; 169 | chart.options.plugins.tooltip.titleColor = tooltipTitleColor.light; 170 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.light; 171 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.light; 172 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.light; 173 | } 174 | chart.update("none"); 175 | }, [currentTheme]); 176 | 177 | return ( 178 | 179 |
          180 |
          181 |
          182 | $57.81 183 |
          184 |
          188 |
          189 |
          190 |
          191 | 192 |
          193 |
          194 | ); 195 | } 196 | 197 | export default RealtimeChart; 198 | -------------------------------------------------------------------------------- /src/css/additional-styles/flatpickr.css: -------------------------------------------------------------------------------- 1 | @import 'flatpickr/dist/flatpickr.min.css'; 2 | 3 | /* Customise flatpickr */ 4 | * { 5 | --calendarPadding: 24px; 6 | --daySize: 36px; 7 | --daysWidth: calc(var(--daySize)*7); 8 | } 9 | 10 | @keyframes fpFadeInDown { 11 | from { 12 | opacity: 0; 13 | transform: translate3d(0, -8px, 0); 14 | } 15 | to { 16 | opacity: 1; 17 | transform: translate3d(0, 0, 0); 18 | } 19 | } 20 | 21 | .flatpickr-calendar { 22 | border: inherit; 23 | @apply bg-white dark:bg-slate-800 rounded shadow-lg border border-slate-200 dark:border-slate-700 left-1/2; 24 | margin-left: calc(calc(var(--daysWidth) + calc(var(--calendarPadding)*2))*0.5*-1); 25 | padding: var(--calendarPadding); 26 | width: calc(var(--daysWidth) + calc(var(--calendarPadding)*2)); 27 | } 28 | 29 | @screen lg { 30 | .flatpickr-calendar { 31 | @apply left-0 right-auto; 32 | margin-left: 0; 33 | } 34 | } 35 | 36 | .flatpickr-right.flatpickr-calendar { 37 | @apply right-0 left-auto; 38 | margin-left: 0; 39 | } 40 | 41 | .flatpickr-calendar.animate.open { 42 | animation: fpFadeInDown 200ms ease-out; 43 | } 44 | 45 | .flatpickr-calendar.static { 46 | position: absolute; 47 | top: calc(100% + 4px); 48 | } 49 | 50 | .flatpickr-calendar.static.open { 51 | z-index: 20; 52 | } 53 | 54 | .flatpickr-days { 55 | width: var(--daysWidth); 56 | } 57 | 58 | .dayContainer { 59 | width: var(--daysWidth); 60 | min-width: var(--daysWidth); 61 | max-width: var(--daysWidth); 62 | } 63 | 64 | .flatpickr-day { 65 | @apply bg-slate-50 dark:bg-slate-700/20 text-sm font-medium text-slate-600 dark:text-slate-100; 66 | max-width: var(--daySize); 67 | height: var(--daySize); 68 | line-height: var(--daySize); 69 | } 70 | 71 | .flatpickr-day, 72 | .flatpickr-day.prevMonthDay, 73 | .flatpickr-day.nextMonthDay { 74 | border: none; 75 | } 76 | 77 | .flatpickr-day.flatpickr-disabled, 78 | .flatpickr-day.flatpickr-disabled:hover, 79 | .flatpickr-day.prevMonthDay, 80 | .flatpickr-day.nextMonthDay, 81 | .flatpickr-day.notAllowed, 82 | .flatpickr-day.notAllowed.prevMonthDay, 83 | .flatpickr-day.notAllowed.nextMonthDay { 84 | @apply bg-transparent; 85 | } 86 | 87 | .flatpickr-day, 88 | .flatpickr-day.prevMonthDay, 89 | .flatpickr-day.nextMonthDay, 90 | .flatpickr-day.selected.startRange, 91 | .flatpickr-day.startRange.startRange, 92 | .flatpickr-day.endRange.startRange, 93 | .flatpickr-day.selected.endRange, 94 | .flatpickr-day.startRange.endRange, 95 | .flatpickr-day.endRange.endRange, 96 | .flatpickr-day.selected.startRange.endRange, 97 | .flatpickr-day.startRange.startRange.endRange, 98 | .flatpickr-day.endRange.startRange.endRange { 99 | border-radius: 0; 100 | } 101 | 102 | .flatpickr-day.flatpickr-disabled, 103 | .flatpickr-day.flatpickr-disabled:hover, 104 | .flatpickr-day.prevMonthDay, 105 | .flatpickr-day.nextMonthDay, 106 | .flatpickr-day.notAllowed, 107 | .flatpickr-day.notAllowed.prevMonthDay, 108 | .flatpickr-day.notAllowed.nextMonthDay { 109 | @apply text-slate-400 dark:text-slate-500; 110 | } 111 | 112 | .rangeMode .flatpickr-day { 113 | margin: 0; 114 | } 115 | 116 | .flatpickr-day.selected, 117 | .flatpickr-day.startRange, 118 | .flatpickr-day.endRange, 119 | .flatpickr-day.selected.inRange, 120 | .flatpickr-day.startRange.inRange, 121 | .flatpickr-day.endRange.inRange, 122 | .flatpickr-day.selected:focus, 123 | .flatpickr-day.startRange:focus, 124 | .flatpickr-day.endRange:focus, 125 | .flatpickr-day.selected:hover, 126 | .flatpickr-day.startRange:hover, 127 | .flatpickr-day.endRange:hover, 128 | .flatpickr-day.selected.prevMonthDay, 129 | .flatpickr-day.startRange.prevMonthDay, 130 | .flatpickr-day.endRange.prevMonthDay, 131 | .flatpickr-day.selected.nextMonthDay, 132 | .flatpickr-day.startRange.nextMonthDay, 133 | .flatpickr-day.endRange.nextMonthDay { 134 | @apply bg-indigo-500 text-indigo-50; 135 | } 136 | 137 | .flatpickr-day.inRange, 138 | .flatpickr-day.prevMonthDay.inRange, 139 | .flatpickr-day.nextMonthDay.inRange, 140 | .flatpickr-day.today.inRange, 141 | .flatpickr-day.prevMonthDay.today.inRange, 142 | .flatpickr-day.nextMonthDay.today.inRange, 143 | .flatpickr-day:hover, 144 | .flatpickr-day.prevMonthDay:hover, 145 | .flatpickr-day.nextMonthDay:hover, 146 | .flatpickr-day:focus, 147 | .flatpickr-day.prevMonthDay:focus, 148 | .flatpickr-day.nextMonthDay:focus, 149 | .flatpickr-day.today:hover, 150 | .flatpickr-day.today:focus { 151 | @apply bg-indigo-400 text-indigo-50; 152 | } 153 | 154 | .flatpickr-day.inRange, 155 | .flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)), 156 | .flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)), 157 | .flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) { 158 | box-shadow: none; 159 | } 160 | 161 | .flatpickr-months { 162 | align-items: center; 163 | margin-top: -8px; 164 | margin-bottom: 6px; 165 | } 166 | 167 | .flatpickr-months .flatpickr-prev-month, 168 | .flatpickr-months .flatpickr-next-month { 169 | position: static; 170 | height: auto; 171 | @apply text-slate-600 hover:text-slate-900 dark:text-slate-500 dark:hover:text-slate-300; 172 | } 173 | 174 | .flatpickr-months .flatpickr-prev-month svg, 175 | .flatpickr-months .flatpickr-next-month svg { 176 | width: 7px; 177 | height: 11px; 178 | } 179 | 180 | .flatpickr-months .flatpickr-prev-month:hover svg, 181 | .flatpickr-months .flatpickr-next-month:hover svg { 182 | @apply fill-current; 183 | } 184 | 185 | .flatpickr-months .flatpickr-prev-month { 186 | margin-left: -10px; 187 | } 188 | 189 | .flatpickr-months .flatpickr-next-month { 190 | margin-right: -10px; 191 | } 192 | 193 | .flatpickr-months .flatpickr-month { 194 | @apply text-slate-800 dark:text-slate-100; 195 | height: auto; 196 | line-height: inherit; 197 | } 198 | 199 | .flatpickr-current-month { 200 | @apply text-sm font-medium; 201 | position: static; 202 | height: auto; 203 | width: auto; 204 | left: auto; 205 | padding: 0; 206 | } 207 | 208 | .flatpickr-current-month span.cur-month { 209 | @apply font-medium m-0; 210 | } 211 | 212 | .flatpickr-current-month span.cur-month:hover { 213 | background: none; 214 | } 215 | 216 | .flatpickr-current-month input.cur-year { 217 | font-weight: inherit; 218 | box-shadow: none !important; 219 | } 220 | 221 | .numInputWrapper:hover { 222 | background: none; 223 | } 224 | 225 | .numInputWrapper span { 226 | display: none; 227 | } 228 | 229 | span.flatpickr-weekday { 230 | @apply text-slate-400 dark:text-slate-500 font-medium text-xs; 231 | } 232 | 233 | .flatpickr-calendar.arrowTop::before, 234 | .flatpickr-calendar.arrowTop::after, 235 | .flatpickr-calendar.arrowBottom::before, 236 | .flatpickr-calendar.arrowBottom::after { 237 | display: none; 238 | } -------------------------------------------------------------------------------- /src/charts/LineChart02.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect, useState } from 'react'; 2 | import { useThemeProvider } from '../utils/ThemeContext'; 3 | 4 | import { chartColors } from './ChartjsConfig'; 5 | import { 6 | Chart, LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip, 7 | } from 'chart.js'; 8 | import 'chartjs-adapter-moment'; 9 | 10 | // Import utilities 11 | import { tailwindConfig, formatValue } from '../utils/Utils'; 12 | 13 | Chart.register(LineController, LineElement, Filler, PointElement, LinearScale, TimeScale, Tooltip); 14 | 15 | function LineChart02({ 16 | data, 17 | width, 18 | height 19 | }) { 20 | 21 | const [chart, setChart] = useState(null) 22 | const canvas = useRef(null); 23 | const legend = useRef(null); 24 | const { currentTheme } = useThemeProvider(); 25 | const darkMode = currentTheme === 'dark'; 26 | const { textColor, gridColor, tooltipBodyColor, tooltipBgColor, tooltipBorderColor } = chartColors; 27 | 28 | useEffect(() => { 29 | const ctx = canvas.current; 30 | // eslint-disable-next-line no-unused-vars 31 | const newChart = new Chart(ctx, { 32 | type: 'line', 33 | data: data, 34 | options: { 35 | layout: { 36 | padding: 20, 37 | }, 38 | scales: { 39 | y: { 40 | border: { 41 | display: false, 42 | }, 43 | beginAtZero: true, 44 | ticks: { 45 | maxTicksLimit: 5, 46 | callback: (value) => formatValue(value), 47 | color: darkMode ? textColor.dark : textColor.light, 48 | }, 49 | grid: { 50 | color: darkMode ? gridColor.dark : gridColor.light, 51 | }, 52 | }, 53 | x: { 54 | type: 'time', 55 | time: { 56 | parser: 'MM-DD-YYYY', 57 | unit: 'month', 58 | displayFormats: { 59 | month: 'MMM YY', 60 | }, 61 | }, 62 | border: { 63 | display: false, 64 | }, 65 | grid: { 66 | display: false, 67 | }, 68 | ticks: { 69 | autoSkipPadding: 48, 70 | maxRotation: 0, 71 | color: darkMode ? textColor.dark : textColor.light, 72 | }, 73 | }, 74 | }, 75 | plugins: { 76 | legend: { 77 | display: false, 78 | }, 79 | tooltip: { 80 | callbacks: { 81 | title: () => false, // Disable tooltip title 82 | label: (context) => formatValue(context.parsed.y), 83 | }, 84 | bodyColor: darkMode ? tooltipBodyColor.dark : tooltipBodyColor.light, 85 | backgroundColor: darkMode ? tooltipBgColor.dark : tooltipBgColor.light, 86 | borderColor: darkMode ? tooltipBorderColor.dark : tooltipBorderColor.light, 87 | }, 88 | }, 89 | interaction: { 90 | intersect: false, 91 | mode: 'nearest', 92 | }, 93 | maintainAspectRatio: false, 94 | resizeDelay: 200, 95 | }, 96 | plugins: [ 97 | { 98 | id: 'htmlLegend', 99 | afterUpdate(c, args, options) { 100 | const ul = legend.current; 101 | if (!ul) return; 102 | // Remove old legend items 103 | while (ul.firstChild) { 104 | ul.firstChild.remove(); 105 | } 106 | // Reuse the built-in legendItems generator 107 | const items = c.options.plugins.legend.labels.generateLabels(c); 108 | items.slice(0, 2).forEach((item) => { 109 | const li = document.createElement('li'); 110 | li.style.marginLeft = tailwindConfig().theme.margin[3]; 111 | // Button element 112 | const button = document.createElement('button'); 113 | button.style.display = 'inline-flex'; 114 | button.style.alignItems = 'center'; 115 | button.style.opacity = item.hidden ? '.3' : ''; 116 | button.onclick = () => { 117 | c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex)); 118 | c.update(); 119 | }; 120 | // Color box 121 | const box = document.createElement('span'); 122 | box.style.display = 'block'; 123 | box.style.width = tailwindConfig().theme.width[3]; 124 | box.style.height = tailwindConfig().theme.height[3]; 125 | box.style.borderRadius = tailwindConfig().theme.borderRadius.full; 126 | box.style.marginRight = tailwindConfig().theme.margin[2]; 127 | box.style.borderWidth = '3px'; 128 | box.style.borderColor = c.data.datasets[item.datasetIndex].borderColor; 129 | box.style.pointerEvents = 'none'; 130 | // Label 131 | const label = document.createElement('span'); 132 | label.classList.add('text-slate-500', 'dark:text-slate-400'); 133 | label.style.fontSize = tailwindConfig().theme.fontSize.sm[0]; 134 | label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight; 135 | const labelText = document.createTextNode(item.text); 136 | label.appendChild(labelText); 137 | li.appendChild(button); 138 | button.appendChild(box); 139 | button.appendChild(label); 140 | ul.appendChild(li); 141 | }); 142 | }, 143 | }, 144 | ], 145 | }); 146 | setChart(newChart); 147 | return () => newChart.destroy(); 148 | // eslint-disable-next-line react-hooks/exhaustive-deps 149 | }, []); 150 | 151 | useEffect(() => { 152 | if (!chart) return; 153 | 154 | if (darkMode) { 155 | chart.options.scales.x.ticks.color = textColor.dark; 156 | chart.options.scales.y.ticks.color = textColor.dark; 157 | chart.options.scales.y.grid.color = gridColor.dark; 158 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.dark; 159 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.dark; 160 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.dark; 161 | } else { 162 | chart.options.scales.x.ticks.color = textColor.light; 163 | chart.options.scales.y.ticks.color = textColor.light; 164 | chart.options.scales.y.grid.color = gridColor.light; 165 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.light; 166 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.light; 167 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.light; 168 | } 169 | chart.update('none'); 170 | }, [currentTheme]); 171 | 172 | return ( 173 | 174 |
          175 |
          176 |
          177 |
          $1,482
          178 |
          -22%
          179 |
          180 |
          181 |
            182 |
            183 |
            184 |
            185 | {/* Chart built with Chart.js 3 */} 186 |
            187 | 188 |
            189 |
            190 | ); 191 | } 192 | 193 | export default LineChart02; -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard13.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function DashboardCard13() { 4 | return ( 5 |
            6 |
            7 |

            Income/Expenses

            8 |
            9 |
            10 | {/* Card content */} 11 | {/* "Today" group */} 12 |
            13 |
            14 | Today 15 |
            16 |
              17 | {/* Item */} 18 |
            • 19 |
              20 | 21 | 22 | 23 |
              24 |
              25 |
              26 |
              27 | 28 | Qonto 29 | {' '} 30 | billing 31 |
              32 |
              33 | -$49.88 34 |
              35 |
              36 |
              37 |
            • 38 | {/* Item */} 39 |
            • 40 |
              41 | 42 | 43 | 44 |
              45 |
              46 |
              47 |
              48 | 49 | Cruip.com 50 | {' '} 51 | Market Ltd 70 Wilson St London 52 |
              53 |
              54 | +249.88 55 |
              56 |
              57 |
              58 |
            • 59 | {/* Item */} 60 |
            • 61 |
              62 | 63 | 64 | 65 |
              66 |
              67 |
              68 | 73 |
              74 | +99.99 75 |
              76 |
              77 |
              78 |
            • 79 | {/* Item */} 80 |
            • 81 |
              82 | 83 | 84 | 85 |
              86 |
              87 |
              88 | 93 |
              94 | +1,200.88 95 |
              96 |
              97 |
              98 |
            • 99 | {/* Item */} 100 |
            • 101 |
              102 | 103 | 104 | 105 |
              106 |
              107 |
              108 |
              109 | 110 | App.com 111 | {' '} 112 | Market Ltd 70 Wilson St London 113 |
              114 |
              115 | +$99.99 116 |
              117 |
              118 |
              119 |
            • 120 | {/* Item */} 121 |
            • 122 |
              123 | 124 | 125 | 126 |
              127 |
              128 |
              129 |
              130 | 131 | App.com 132 | {' '} 133 | Market Ltd 70 Wilson St London 134 |
              135 |
              136 | -$49.88 137 |
              138 |
              139 |
              140 |
            • 141 |
            142 |
            143 |
            144 |
            145 | ); 146 | } 147 | 148 | export default DashboardCard13; 149 | -------------------------------------------------------------------------------- /src/charts/BarChart01.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect, useState } from 'react'; 2 | import { useThemeProvider } from '../utils/ThemeContext'; 3 | 4 | import { chartColors } from './ChartjsConfig'; 5 | import { 6 | Chart, BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend, 7 | } from 'chart.js'; 8 | import 'chartjs-adapter-moment'; 9 | 10 | // Import utilities 11 | import { tailwindConfig, formatValue } from '../utils/Utils'; 12 | 13 | Chart.register(BarController, BarElement, LinearScale, TimeScale, Tooltip, Legend); 14 | 15 | function BarChart01({ 16 | data, 17 | width, 18 | height 19 | }) { 20 | 21 | const [chart, setChart] = useState(null) 22 | const canvas = useRef(null); 23 | const legend = useRef(null); 24 | const { currentTheme } = useThemeProvider(); 25 | const darkMode = currentTheme === 'dark'; 26 | const { textColor, gridColor, tooltipBodyColor, tooltipBgColor, tooltipBorderColor } = chartColors; 27 | 28 | useEffect(() => { 29 | const ctx = canvas.current; 30 | // eslint-disable-next-line no-unused-vars 31 | const newChart = new Chart(ctx, { 32 | type: 'bar', 33 | data: data, 34 | options: { 35 | layout: { 36 | padding: { 37 | top: 12, 38 | bottom: 16, 39 | left: 20, 40 | right: 20, 41 | }, 42 | }, 43 | scales: { 44 | y: { 45 | border: { 46 | display: false, 47 | }, 48 | ticks: { 49 | maxTicksLimit: 5, 50 | callback: (value) => formatValue(value), 51 | color: darkMode ? textColor.dark : textColor.light, 52 | }, 53 | grid: { 54 | color: darkMode ? gridColor.dark : gridColor.light, 55 | }, 56 | }, 57 | x: { 58 | type: 'time', 59 | time: { 60 | parser: 'MM-DD-YYYY', 61 | unit: 'month', 62 | displayFormats: { 63 | month: 'MMM YY', 64 | }, 65 | }, 66 | border: { 67 | display: false, 68 | }, 69 | grid: { 70 | display: false, 71 | }, 72 | ticks: { 73 | color: darkMode ? textColor.dark : textColor.light, 74 | }, 75 | }, 76 | }, 77 | plugins: { 78 | legend: { 79 | display: false, 80 | }, 81 | tooltip: { 82 | callbacks: { 83 | title: () => false, // Disable tooltip title 84 | label: (context) => formatValue(context.parsed.y), 85 | }, 86 | bodyColor: darkMode ? tooltipBodyColor.dark : tooltipBodyColor.light, 87 | backgroundColor: darkMode ? tooltipBgColor.dark : tooltipBgColor.light, 88 | borderColor: darkMode ? tooltipBorderColor.dark : tooltipBorderColor.light, 89 | }, 90 | }, 91 | interaction: { 92 | intersect: false, 93 | mode: 'nearest', 94 | }, 95 | animation: { 96 | duration: 500, 97 | }, 98 | maintainAspectRatio: false, 99 | resizeDelay: 200, 100 | }, 101 | plugins: [ 102 | { 103 | id: 'htmlLegend', 104 | afterUpdate(c, args, options) { 105 | const ul = legend.current; 106 | if (!ul) return; 107 | // Remove old legend items 108 | while (ul.firstChild) { 109 | ul.firstChild.remove(); 110 | } 111 | // Reuse the built-in legendItems generator 112 | const items = c.options.plugins.legend.labels.generateLabels(c); 113 | items.forEach((item) => { 114 | const li = document.createElement('li'); 115 | li.style.marginRight = tailwindConfig().theme.margin[4]; 116 | // Button element 117 | const button = document.createElement('button'); 118 | button.style.display = 'inline-flex'; 119 | button.style.alignItems = 'center'; 120 | button.style.opacity = item.hidden ? '.3' : ''; 121 | button.onclick = () => { 122 | c.setDatasetVisibility(item.datasetIndex, !c.isDatasetVisible(item.datasetIndex)); 123 | c.update(); 124 | }; 125 | // Color box 126 | const box = document.createElement('span'); 127 | box.style.display = 'block'; 128 | box.style.width = tailwindConfig().theme.width[3]; 129 | box.style.height = tailwindConfig().theme.height[3]; 130 | box.style.borderRadius = tailwindConfig().theme.borderRadius.full; 131 | box.style.marginRight = tailwindConfig().theme.margin[2]; 132 | box.style.borderWidth = '3px'; 133 | box.style.borderColor = item.fillStyle; 134 | box.style.pointerEvents = 'none'; 135 | // Label 136 | const labelContainer = document.createElement('span'); 137 | labelContainer.style.display = 'flex'; 138 | labelContainer.style.alignItems = 'center'; 139 | const value = document.createElement('span'); 140 | value.classList.add('text-slate-800', 'dark:text-slate-100'); 141 | value.style.fontSize = tailwindConfig().theme.fontSize['3xl'][0]; 142 | value.style.lineHeight = tailwindConfig().theme.fontSize['3xl'][1].lineHeight; 143 | value.style.fontWeight = tailwindConfig().theme.fontWeight.bold; 144 | value.style.marginRight = tailwindConfig().theme.margin[2]; 145 | value.style.pointerEvents = 'none'; 146 | const label = document.createElement('span'); 147 | label.classList.add('text-slate-500', 'dark:text-slate-400'); 148 | label.style.fontSize = tailwindConfig().theme.fontSize.sm[0]; 149 | label.style.lineHeight = tailwindConfig().theme.fontSize.sm[1].lineHeight; 150 | const theValue = c.data.datasets[item.datasetIndex].data.reduce((a, b) => a + b, 0); 151 | const valueText = document.createTextNode(formatValue(theValue)); 152 | const labelText = document.createTextNode(item.text); 153 | value.appendChild(valueText); 154 | label.appendChild(labelText); 155 | li.appendChild(button); 156 | button.appendChild(box); 157 | button.appendChild(labelContainer); 158 | labelContainer.appendChild(value); 159 | labelContainer.appendChild(label); 160 | ul.appendChild(li); 161 | }); 162 | }, 163 | }, 164 | ], 165 | }); 166 | setChart(newChart); 167 | return () => newChart.destroy(); 168 | // eslint-disable-next-line react-hooks/exhaustive-deps 169 | }, []); 170 | 171 | useEffect(() => { 172 | if (!chart) return; 173 | 174 | if (darkMode) { 175 | chart.options.scales.x.ticks.color = textColor.dark; 176 | chart.options.scales.y.ticks.color = textColor.dark; 177 | chart.options.scales.y.grid.color = gridColor.dark; 178 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.dark; 179 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.dark; 180 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.dark; 181 | } else { 182 | chart.options.scales.x.ticks.color = textColor.light; 183 | chart.options.scales.y.ticks.color = textColor.light; 184 | chart.options.scales.y.grid.color = gridColor.light; 185 | chart.options.plugins.tooltip.bodyColor = tooltipBodyColor.light; 186 | chart.options.plugins.tooltip.backgroundColor = tooltipBgColor.light; 187 | chart.options.plugins.tooltip.borderColor = tooltipBorderColor.light; 188 | } 189 | chart.update('none'); 190 | }, [currentTheme]); 191 | 192 | return ( 193 | 194 |
            195 |
              196 |
              197 |
              198 | 199 |
              200 |
              201 | ); 202 | } 203 | 204 | export default BarChart01; 205 | -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard07.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function DashboardCard07() { 4 | return ( 5 |
              6 |
              7 |

              Top Channels

              8 |
              9 |
              10 | {/* Table */} 11 |
              12 | 13 | {/* Table header */} 14 | 15 | 16 | 19 | 22 | 25 | 28 | 31 | 32 | 33 | {/* Table body */} 34 | 35 | {/* Row */} 36 | 37 | 49 | 52 | 55 | 58 | 61 | 62 | {/* Row */} 63 | 64 | 77 | 80 | 83 | 86 | 89 | 90 | {/* Row */} 91 | 92 | 105 | 108 | 111 | 114 | 117 | 118 | {/* Row */} 119 | 120 | 133 | 136 | 139 | 142 | 145 | 146 | {/* Row */} 147 | 148 | 160 | 163 | 166 | 169 | 172 | 173 | 174 |
              17 |
              Source
              18 |
              20 |
              Visitors
              21 |
              23 |
              Revenues
              24 |
              26 |
              Sales
              27 |
              29 |
              Conversion
              30 |
              38 |
              39 | 40 | 41 | 45 | 46 |
              Github.com
              47 |
              48 |
              50 |
              2.4K
              51 |
              53 |
              $3,877
              54 |
              56 |
              267
              57 |
              59 |
              4.7%
              60 |
              65 |
              66 | 67 | 68 | 73 | 74 |
              Twitter
              75 |
              76 |
              78 |
              2.2K
              79 |
              81 |
              $3,426
              82 |
              84 |
              249
              85 |
              87 |
              4.4%
              88 |
              93 |
              94 | 95 | 96 | 101 | 102 |
              Google (organic)
              103 |
              104 |
              106 |
              2.0K
              107 |
              109 |
              $2,444
              110 |
              112 |
              224
              113 |
              115 |
              4.2%
              116 |
              121 |
              122 | 123 | 124 | 129 | 130 |
              Vimeo.com
              131 |
              132 |
              134 |
              1.9K
              135 |
              137 |
              $2,236
              138 |
              140 |
              220
              141 |
              143 |
              4.2%
              144 |
              149 |
              150 | 151 | 152 | 156 | 157 |
              Indiehackers.com
              158 |
              159 |
              161 |
              1.7K
              162 |
              164 |
              $2,034
              165 |
              167 |
              204
              168 |
              170 |
              3.9%
              171 |
              175 |
              176 |
              177 |
              178 | ); 179 | } 180 | 181 | export default DashboardCard07; 182 | -------------------------------------------------------------------------------- /src/partials/dashboard/DashboardCard12.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function DashboardCard12() { 4 | return ( 5 |
              6 |
              7 |

              Recent Activity

              8 |
              9 |
              10 | {/* Card content */} 11 | {/* "Today" group */} 12 |
              13 |
              14 | Today 15 |
              16 | 98 |
              99 | {/* "Yesterday" group */} 100 |
              101 |
              102 | Yesterday 103 |
              104 | 159 |
              160 |
              161 |
              162 | ); 163 | } 164 | 165 | export default DashboardCard12; 166 | -------------------------------------------------------------------------------- /src/components/ModalSearch.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import Transition from '../utils/Transition'; 4 | 5 | function ModalSearch({ 6 | id, 7 | searchId, 8 | modalOpen, 9 | setModalOpen 10 | }) { 11 | 12 | const modalContent = useRef(null); 13 | const searchInput = useRef(null); 14 | 15 | // close on click outside 16 | useEffect(() => { 17 | const clickHandler = ({ target }) => { 18 | if (!modalOpen || modalContent.current.contains(target)) return 19 | setModalOpen(false); 20 | }; 21 | document.addEventListener('click', clickHandler); 22 | return () => document.removeEventListener('click', clickHandler); 23 | }); 24 | 25 | // close if the esc key is pressed 26 | useEffect(() => { 27 | const keyHandler = ({ keyCode }) => { 28 | if (!modalOpen || keyCode !== 27) return; 29 | setModalOpen(false); 30 | }; 31 | document.addEventListener('keydown', keyHandler); 32 | return () => document.removeEventListener('keydown', keyHandler); 33 | }); 34 | 35 | useEffect(() => { 36 | modalOpen && searchInput.current.focus(); 37 | }, [modalOpen]); 38 | 39 | return ( 40 | <> 41 | {/* Modal backdrop */} 42 |