├── 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 | You need to enable JavaScript to run this app.
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 | 
8 |
9 | 
10 |
11 | 
12 |
13 | 
14 |
15 | 
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 |
e.preventDefault()}
25 | >
26 |
27 |
28 |
29 |
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 |
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 |
19 |
setBannerOpen(false) }>
20 | Close
21 |
22 |
23 |
24 |
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 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Add new user
34 |
35 |
36 |
37 |
38 |
39 |
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 |
21 |
22 |
26 |
27 |
28 |
29 |
33 |
37 |
38 | Switch to light / dark version
39 |
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 |
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 |
setDropdownOpen(!dropdownOpen)}
47 | aria-expanded={dropdownOpen}
48 | >
49 | Menu
50 |
51 |
52 |
53 |
54 |
55 |
56 |
69 | setDropdownOpen(true)} onBlur={() => setDropdownOpen(false)}>
70 | {children}
71 |
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 |
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 |
e.preventDefault()}>
73 |
74 |
75 |
76 |
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 | {
24 | e.stopPropagation();
25 | setSidebarOpen(!sidebarOpen);
26 | }}
27 | >
28 | Open sidebar
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {/* Header: Right side */}
38 |
39 |
40 |
{
45 | e.stopPropagation();
46 | setSearchModalOpen(true);
47 | }}
48 | aria-controls="search-modal"
49 | >
50 | Search
51 |
52 |
56 |
60 |
61 |
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 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
85 |
86 |
87 |
88 |
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 |
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 |
setDropdownOpen(!dropdownOpen)}
46 | aria-expanded={dropdownOpen}
47 | >
48 |
55 |
56 |
57 | Dan-Codes Inc.
58 |
59 |
63 |
64 |
65 |
66 |
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 |
59 |
60 |
61 | {/* Table */}
62 |
63 |
64 | {/* Table header */}
65 |
66 |
67 |
68 | Name
69 |
70 |
71 | Email
72 |
73 |
74 | Spent
75 |
76 |
77 | Country
78 |
79 |
80 |
81 | {/* Table body */}
82 |
83 | {
84 | customers.map(customer => {
85 | return (
86 |
87 |
88 |
89 |
90 |
91 |
92 |
{customer.name}
93 |
94 |
95 |
96 | {customer.email}
97 |
98 |
99 | {customer.spent}
100 |
101 |
102 | {customer.location}
103 |
104 |
105 | )
106 | })
107 | }
108 |
109 |
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 |
setDropdownOpen(!dropdownOpen)}
64 | aria-expanded={dropdownOpen}
65 | >
66 |
67 |
68 |
69 |
70 | {options[selected].period}
71 |
72 |
73 |
74 |
75 |
76 |
87 | setDropdownOpen(true)}
91 | onBlur={() => setDropdownOpen(false)}
92 | >
93 | {
94 | options.map(option => {
95 | return (
96 |
{ setSelected(option.id); setDropdownOpen(false); }}
101 | >
102 |
103 |
104 |
105 | {option.period}
106 |
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 |
54 |
58 |
59 |
60 | Add view
61 |
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 |
setDropdownOpen(!dropdownOpen)}
42 | aria-expanded={dropdownOpen}
43 | >
44 | Need help?
45 |
46 |
47 |
48 |
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 |
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 |
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 |
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 |
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 |
setDropdownOpen(!dropdownOpen)}
42 | aria-expanded={dropdownOpen}
43 | >
44 | Notifications
45 |
46 |
47 |
48 |
49 |
50 |
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 |
setDropdownOpen(!dropdownOpen)}
41 | aria-expanded={dropdownOpen}
42 | >
43 | Filter
44 |
45 |
46 |
47 |
48 |
49 |
62 |
63 |
Filters
64 |
102 |
103 |
104 |
105 |
106 | Clear
107 |
108 |
109 |
110 | setDropdownOpen(false)}
113 | onBlur={() => setDropdownOpen(false)}
114 | >
115 | Apply
116 |
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 |
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 |
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 |
9 |
10 | {/* Card content */}
11 | {/* "Today" group */}
12 |
13 |
16 |
17 | {/* Item */}
18 |
19 |
24 |
25 |
26 |
32 |
33 | -$49.88
34 |
35 |
36 |
37 |
38 | {/* Item */}
39 |
40 |
45 |
46 |
47 |
53 |
54 | +249.88
55 |
56 |
57 |
58 |
59 | {/* Item */}
60 |
61 |
66 |
67 |
68 |
73 |
74 | +99.99
75 |
76 |
77 |
78 |
79 | {/* Item */}
80 |
81 |
86 |
87 |
88 |
93 |
94 | +1,200.88
95 |
96 |
97 |
98 |
99 | {/* Item */}
100 |
101 |
106 |
107 |
108 |
114 |
115 | +$99.99
116 |
117 |
118 |
119 |
120 | {/* Item */}
121 |
122 |
127 |
128 |
129 |
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 |
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 |
9 |
10 | {/* Table */}
11 |
12 |
13 | {/* Table header */}
14 |
15 |
16 |
17 | Source
18 |
19 |
20 | Visitors
21 |
22 |
23 | Revenues
24 |
25 |
26 | Sales
27 |
28 |
29 | Conversion
30 |
31 |
32 |
33 | {/* Table body */}
34 |
35 | {/* Row */}
36 |
37 |
38 |
39 |
40 |
41 |
45 |
46 |
Github.com
47 |
48 |
49 |
50 | 2.4K
51 |
52 |
53 | $3,877
54 |
55 |
56 | 267
57 |
58 |
59 | 4.7%
60 |
61 |
62 | {/* Row */}
63 |
64 |
65 |
66 |
67 |
68 |
73 |
74 |
Twitter
75 |
76 |
77 |
78 | 2.2K
79 |
80 |
81 | $3,426
82 |
83 |
84 | 249
85 |
86 |
87 | 4.4%
88 |
89 |
90 | {/* Row */}
91 |
92 |
93 |
94 |
95 |
96 |
101 |
102 |
Google (organic)
103 |
104 |
105 |
106 | 2.0K
107 |
108 |
109 | $2,444
110 |
111 |
112 | 224
113 |
114 |
115 | 4.2%
116 |
117 |
118 | {/* Row */}
119 |
120 |
121 |
122 |
123 |
124 |
129 |
130 |
Vimeo.com
131 |
132 |
133 |
134 | 1.9K
135 |
136 |
137 | $2,236
138 |
139 |
140 | 220
141 |
142 |
143 | 4.2%
144 |
145 |
146 | {/* Row */}
147 |
148 |
149 |
150 |
151 |
152 |
156 |
157 |
Indiehackers.com
158 |
159 |
160 |
161 | 1.7K
162 |
163 |
164 | $2,034
165 |
166 |
167 | 204
168 |
169 |
170 | 3.9%
171 |
172 |
173 |
174 |
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 |
9 |
10 | {/* Card content */}
11 | {/* "Today" group */}
12 |
13 |
16 |
17 | {/* Item */}
18 |
19 |
24 |
43 |
44 | {/* Item */}
45 |
46 |
51 |
70 |
71 | {/* Item */}
72 |
73 |
78 |
96 |
97 |
98 |
99 | {/* "Yesterday" group */}
100 |
101 |
104 |
105 | {/* Item */}
106 |
107 |
112 |
130 |
131 | {/* Item */}
132 |
133 |
138 |
157 |
158 |
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 |
53 | {/* Modal dialog */}
54 |
67 |
71 | {/* Search form */}
72 |
96 |
97 | {/* Recent searches */}
98 |
99 |
Recent searches
100 |
101 |
102 | setModalOpen(!modalOpen)}
106 | >
107 |
111 |
112 |
113 | Form Builder - 23 hours on-demand video
114 |
115 |
116 |
117 | setModalOpen(!modalOpen)}
121 | >
122 |
126 |
127 |
128 | Access Mosaic on mobile and TV
129 |
130 |
131 |
132 | setModalOpen(!modalOpen)}
136 | >
137 |
141 |
142 |
143 | Product Update - Q4 2021
144 |
145 |
146 |
147 | setModalOpen(!modalOpen)}
151 | >
152 |
156 |
157 |
158 | Master Digital Marketing Strategy course
159 |
160 |
161 |
162 | setModalOpen(!modalOpen)}
166 | >
167 |
171 |
172 |
173 | Dedicated forms for products
174 |
175 |
176 |
177 | setModalOpen(!modalOpen)}
181 | >
182 |
186 |
187 |
188 | Product Update - Q4 2021
189 |
190 |
191 |
192 |
193 | {/* Recent pages */}
194 |
195 |
Recent pages
196 |
197 |
198 | setModalOpen(!modalOpen)}
202 | >
203 |
207 |
208 |
209 |
210 | Messages -{' '}
211 | Conversation / … / Mike Mills
212 |
213 |
214 |
215 |
216 | setModalOpen(!modalOpen)}
220 | >
221 |
225 |
226 |
227 |
228 | Messages -{' '}
229 | Conversation / … / Eva Patrick
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 | >
239 | );
240 | }
241 |
242 | export default ModalSearch;
243 |
--------------------------------------------------------------------------------