├── .gitignore
├── .prettierignore
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── eslint.config.mjs
├── index.html
├── package-lock.json
├── package.json
├── prettier.config.mjs
├── public
├── assets
│ ├── background
│ │ ├── overlay.jpg
│ │ └── shape-square.svg
│ ├── icons
│ │ ├── flags
│ │ │ ├── ic-flag-de.svg
│ │ │ ├── ic-flag-en.svg
│ │ │ └── ic-flag-fr.svg
│ │ ├── glass
│ │ │ ├── ic-glass-bag.svg
│ │ │ ├── ic-glass-buy.svg
│ │ │ ├── ic-glass-message.svg
│ │ │ └── ic-glass-users.svg
│ │ ├── navbar
│ │ │ ├── ic-analytics.svg
│ │ │ ├── ic-blog.svg
│ │ │ ├── ic-cart.svg
│ │ │ ├── ic-disabled.svg
│ │ │ ├── ic-lock.svg
│ │ │ └── ic-user.svg
│ │ ├── notification
│ │ │ ├── ic-notification-chat.svg
│ │ │ ├── ic-notification-mail.svg
│ │ │ ├── ic-notification-package.svg
│ │ │ └── ic-notification-shipping.svg
│ │ ├── shape-avatar.svg
│ │ └── workspaces
│ │ │ ├── logo-1.webp
│ │ │ ├── logo-2.webp
│ │ │ └── logo-3.webp
│ ├── illustrations
│ │ ├── illustration-404.svg
│ │ └── illustration-dashboard.webp
│ └── images
│ │ ├── avatar
│ │ ├── avatar-1.webp
│ │ ├── avatar-10.webp
│ │ ├── avatar-11.webp
│ │ ├── avatar-12.webp
│ │ ├── avatar-13.webp
│ │ ├── avatar-14.webp
│ │ ├── avatar-15.webp
│ │ ├── avatar-16.webp
│ │ ├── avatar-17.webp
│ │ ├── avatar-18.webp
│ │ ├── avatar-19.webp
│ │ ├── avatar-2.webp
│ │ ├── avatar-20.webp
│ │ ├── avatar-21.webp
│ │ ├── avatar-22.webp
│ │ ├── avatar-23.webp
│ │ ├── avatar-24.webp
│ │ ├── avatar-25.webp
│ │ ├── avatar-3.webp
│ │ ├── avatar-4.webp
│ │ ├── avatar-5.webp
│ │ ├── avatar-6.webp
│ │ ├── avatar-7.webp
│ │ ├── avatar-8.webp
│ │ └── avatar-9.webp
│ │ ├── cover
│ │ ├── cover-1.webp
│ │ ├── cover-10.webp
│ │ ├── cover-11.webp
│ │ ├── cover-12.webp
│ │ ├── cover-13.webp
│ │ ├── cover-14.webp
│ │ ├── cover-15.webp
│ │ ├── cover-16.webp
│ │ ├── cover-17.webp
│ │ ├── cover-18.webp
│ │ ├── cover-19.webp
│ │ ├── cover-2.webp
│ │ ├── cover-20.webp
│ │ ├── cover-21.webp
│ │ ├── cover-22.webp
│ │ ├── cover-23.webp
│ │ ├── cover-24.webp
│ │ ├── cover-3.webp
│ │ ├── cover-4.webp
│ │ ├── cover-5.webp
│ │ ├── cover-6.webp
│ │ ├── cover-7.webp
│ │ ├── cover-8.webp
│ │ └── cover-9.webp
│ │ ├── minimal-free-preview.jpg
│ │ └── product
│ │ ├── product-1.webp
│ │ ├── product-10.webp
│ │ ├── product-11.webp
│ │ ├── product-12.webp
│ │ ├── product-13.webp
│ │ ├── product-14.webp
│ │ ├── product-15.webp
│ │ ├── product-16.webp
│ │ ├── product-17.webp
│ │ ├── product-18.webp
│ │ ├── product-19.webp
│ │ ├── product-2.webp
│ │ ├── product-20.webp
│ │ ├── product-21.webp
│ │ ├── product-22.webp
│ │ ├── product-23.webp
│ │ ├── product-24.webp
│ │ ├── product-3.webp
│ │ ├── product-4.webp
│ │ ├── product-5.webp
│ │ ├── product-6.webp
│ │ ├── product-7.webp
│ │ ├── product-8.webp
│ │ └── product-9.webp
└── favicon.ico
├── src
├── _mock
│ ├── _data.ts
│ ├── _mock.ts
│ └── index.ts
├── app.tsx
├── components
│ ├── chart
│ │ ├── chart.tsx
│ │ ├── classes.ts
│ │ ├── components
│ │ │ ├── chart-legends.tsx
│ │ │ ├── chart-loading.tsx
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── styles.css
│ │ ├── types.ts
│ │ └── use-chart.ts
│ ├── color-utils
│ │ ├── classes.ts
│ │ ├── color-picker.tsx
│ │ ├── color-preview.tsx
│ │ └── index.ts
│ ├── iconify
│ │ ├── classes.ts
│ │ ├── icon-sets.ts
│ │ ├── iconify.tsx
│ │ ├── index.ts
│ │ └── register-icons.ts
│ ├── label
│ │ ├── classes.ts
│ │ ├── index.ts
│ │ ├── label.tsx
│ │ ├── styles.tsx
│ │ └── types.ts
│ ├── logo
│ │ ├── classes.ts
│ │ ├── index.ts
│ │ └── logo.tsx
│ ├── scrollbar
│ │ ├── classes.ts
│ │ ├── index.ts
│ │ ├── scrollbar.tsx
│ │ ├── styles.css
│ │ └── types.ts
│ └── svg-color
│ │ ├── classes.ts
│ │ ├── index.ts
│ │ ├── svg-color.tsx
│ │ └── types.ts
├── config-global.ts
├── global.css
├── layouts
│ ├── auth
│ │ ├── content.tsx
│ │ ├── index.ts
│ │ └── layout.tsx
│ ├── components
│ │ ├── account-popover.tsx
│ │ ├── language-popover.tsx
│ │ ├── menu-button.tsx
│ │ ├── nav-upgrade.tsx
│ │ ├── notifications-popover.tsx
│ │ ├── searchbar.tsx
│ │ └── workspaces-popover.tsx
│ ├── core
│ │ ├── classes.ts
│ │ ├── css-vars.ts
│ │ ├── header-section.tsx
│ │ ├── index.ts
│ │ ├── layout-section.tsx
│ │ └── main-section.tsx
│ ├── dashboard
│ │ ├── content.tsx
│ │ ├── css-vars.ts
│ │ ├── index.ts
│ │ ├── layout.tsx
│ │ └── nav.tsx
│ ├── nav-config-account.tsx
│ ├── nav-config-dashboard.tsx
│ └── nav-config-workspace.tsx
├── main.tsx
├── pages
│ ├── blog.tsx
│ ├── dashboard.tsx
│ ├── page-not-found.tsx
│ ├── products.tsx
│ ├── sign-in.tsx
│ └── user.tsx
├── routes
│ ├── components
│ │ ├── error-boundary.tsx
│ │ ├── index.ts
│ │ └── router-link.tsx
│ ├── hooks
│ │ ├── index.ts
│ │ ├── use-pathname.ts
│ │ └── use-router.ts
│ └── sections.tsx
├── sections
│ ├── auth
│ │ ├── index.ts
│ │ └── sign-in-view.tsx
│ ├── blog
│ │ ├── post-item.tsx
│ │ ├── post-search.tsx
│ │ ├── post-sort.tsx
│ │ └── view
│ │ │ ├── blog-view.tsx
│ │ │ └── index.ts
│ ├── error
│ │ ├── index.ts
│ │ └── not-found-view.tsx
│ ├── overview
│ │ ├── analytics-conversion-rates.tsx
│ │ ├── analytics-current-subject.tsx
│ │ ├── analytics-current-visits.tsx
│ │ ├── analytics-news.tsx
│ │ ├── analytics-order-timeline.tsx
│ │ ├── analytics-tasks.tsx
│ │ ├── analytics-traffic-by-site.tsx
│ │ ├── analytics-website-visits.tsx
│ │ ├── analytics-widget-summary.tsx
│ │ └── view
│ │ │ ├── index.ts
│ │ │ └── overview-analytics-view.tsx
│ ├── product
│ │ ├── product-cart-widget.tsx
│ │ ├── product-filters.tsx
│ │ ├── product-item.tsx
│ │ ├── product-sort.tsx
│ │ └── view
│ │ │ ├── index.ts
│ │ │ └── products-view.tsx
│ └── user
│ │ ├── table-empty-rows.tsx
│ │ ├── table-no-data.tsx
│ │ ├── user-table-head.tsx
│ │ ├── user-table-row.tsx
│ │ ├── user-table-toolbar.tsx
│ │ ├── utils.ts
│ │ └── view
│ │ ├── index.ts
│ │ └── user-view.tsx
├── theme
│ ├── core
│ │ ├── components.tsx
│ │ ├── custom-shadows.ts
│ │ ├── index.ts
│ │ ├── palette.ts
│ │ ├── shadows.ts
│ │ └── typography.ts
│ ├── create-classes.ts
│ ├── create-theme.ts
│ ├── extend-theme-types.d.ts
│ ├── index.ts
│ ├── theme-config.ts
│ ├── theme-provider.tsx
│ └── types.ts
├── utils
│ ├── format-number.ts
│ └── format-time.ts
└── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
├── vercel.json
├── vite.config.ts
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # dependencies
9 | node_modules
10 | .pnp
11 | .pnp.js
12 |
13 | # testing
14 | coverage
15 |
16 | # production
17 | .next
18 | .swc
19 | _static
20 | out
21 | dist
22 | build
23 |
24 | # environment variables
25 | .env
26 | .env.local
27 | .env.development.local
28 | .env.test.local
29 | .env.production.local
30 |
31 | # misc
32 | .DS_Store
33 | .vercel
34 | .netlify
35 | .vscode
36 | tsconfig.tsbuildinfo
37 | .ncurc.js
38 | knip.jsonc
39 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Build directories
2 | build/*
3 | dist/*
4 | public/*
5 | **/out/*
6 | **/.next/*
7 | **/node_modules/*
8 |
9 | yarn.lock
10 | package-lock.json
11 | jsconfig.json
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### v3.0.0
2 |
3 | ###### Apr 3, 2025
4 |
5 | - Support MUI v7.
6 | - Support React v19.
7 | - Support Eslint v9.
8 | - Upgrade and restructure the directory.
9 | - Upgrade some dependencies to the latest versions.
10 |
11 | ---
12 |
13 | ### v2.0.0
14 |
15 | ###### Aug 24, 2024
16 |
17 | - [New] Migrate to typescript.
18 | - Upgrade and restructure the directory.
19 | - Upgrade some dependencies to the latest versions.
20 |
21 | ---
22 |
23 | ### v1.8.0
24 |
25 | ###### Wed 11, 2023
26 |
27 | - [New] Migrate to vite.js.
28 | - Upgrade and restructure the directory.
29 | - Upgrade some dependencies to the latest versions
30 |
31 | ---
32 |
33 | ### v1.7.0
34 |
35 | ###### Feb 21, 2023
36 |
37 | - Upgrade some dependencies to the latest versions
38 |
39 | ---
40 |
41 | ### v1.6.0
42 |
43 | ###### Oct 17, 2022
44 |
45 | - Upgrade and restructure the directory.
46 | - Upgrade some dependencies to the latest versions
47 |
48 | ---
49 |
50 | ### v1.5.0
51 |
52 | ###### Jul 04, 2022
53 |
54 | - Support react 18.
55 | - Upgrade some dependencies to the latest versions
56 |
57 | ---
58 |
59 | ### v1.4.0
60 |
61 | ###### Apr 12, 2022
62 |
63 | - Update `src/components`.
64 | - Update `src/sections`.
65 | - Update `src/pages`.
66 | - Update `src/layouts`.
67 | - Update `src/theme`.
68 | - Upgrade some dependencies to the latest versions
69 |
70 | ---
71 |
72 | ### v1.3.0
73 |
74 | ###### Feb 21, 2022
75 |
76 | - Support react-script v5.0.0
77 | - Source code improvement
78 | - Upgrade some dependencies to the latest versions
79 |
80 | ---
81 |
82 | ### v1.2.0
83 |
84 | ###### Sep 18, 2021
85 |
86 | - Support MIU v5.0.0 official release
87 | - Upgrade some dependencies to the latest versions
88 | - Update `src/theme/typography.js`
89 | - Upgrade some dependencies to the latest versions
90 |
91 | ---
92 |
93 | ### v1.1.0
94 |
95 | ###### Jul 23, 2021
96 |
97 | - Support MUI v5.0.0-beta.1
98 | - Upgrade some dependencies to the latest versions
99 |
100 | ---
101 |
102 | ### v1.0.0
103 |
104 | ###### Jun 28, 2021
105 |
106 | Initial release.
107 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Minimal UI ([https://minimals.cc/](https://minimals.cc/))
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Minimal UI ([Free version](https://free.minimals.cc/))
2 |
3 | 
4 |
5 | 
6 |
7 | > Free React Admin Dashboard made with Material-UI components and React + Vite.js.
8 |
9 | ## Pages
10 |
11 | - [Dashboard](https://free.minimals.cc/)
12 | - [Users](https://free.minimals.cc/user)
13 | - [Products](https://free.minimals.cc/products)
14 | - [Blog](https://free.minimals.cc/blog)
15 | - [Sign in](https://free.minimals.cc/sign-in)
16 | - [Not found](https://free.minimals.cc/404)
17 |
18 | ## Quick start
19 |
20 | - Clone the repo: `git clone https://github.com/minimal-ui-kit/material-kit-react.git`
21 | - Recommended: `Node.js v20.x`
22 | - **Install:** `npm i` or `yarn install`
23 | - **Start:** `npm run dev` or `yarn dev`
24 | - **Build:** `npm run build` or `yarn build`
25 | - Open browser: `http://localhost:3039`
26 |
27 | ## Upgrade to PRO Version
28 |
29 | | Minimal Free | [Minimal Pro](https://material-ui.com/store/items/minimal-dashboard/) |
30 | | :-------------------------- | :------------------------------------------------------------------------------------------------------ |
31 | | **6** Pages | **70+** Pages |
32 | | **Partial** theme customize | **Fully** theme customize |
33 | | - | **Next.js** version |
34 | | - | **TypeScript** version (Standard Plus and Extended license) |
35 | | - | Design **Figma** file (Standard Plus and Extended license) |
36 | | - | Authentication with **Amplify**, **Auth0**, **JWT**, **Firebase** and **Supabase** |
37 | | - | Light/dark mode, right-to-left, form validation... ([+more components](https://minimals.cc/components)) |
38 | | - | Complete users flows |
39 | | - | 1 year of free updates / 6 months of technical support |
40 | | - | Learn more: [Package & license](https://docs.minimals.cc/package) |
41 |
42 | ## License
43 |
44 | Distributed under the [MIT](https://github.com/minimal-ui-kit/minimal.free/blob/main/LICENSE.md) license.
45 |
46 | ## Contact us
47 |
48 | Email: support@minimals.cc
49 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Minimal UI Kit
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@minimal/material-kit-react",
3 | "author": "minimals.cc",
4 | "licence": "MIT",
5 | "version": "3.0.0",
6 | "private": false,
7 | "type": "module",
8 | "scripts": {
9 | "dev": "vite",
10 | "start": "vite preview",
11 | "build": "tsc && vite build",
12 | "lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"",
13 | "lint:fix": "eslint --fix \"src/**/*.{js,jsx,ts,tsx}\"",
14 | "lint:print": "npx eslint --print-config eslint.config.mjs > eslint-show-config.json",
15 | "fm:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"",
16 | "fm:fix": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
17 | "fix:all": "npm run lint:fix && npm run fm:fix",
18 | "clean": "rm -rf node_modules .next out dist build",
19 | "re:dev": "yarn clean && yarn install && yarn dev",
20 | "re:build": "yarn clean && yarn install && yarn build",
21 | "re:build-npm": "npm run clean && npm install && npm run build",
22 | "tsc:dev": "yarn dev & yarn tsc:watch",
23 | "tsc:watch": "tsc --noEmit --watch",
24 | "tsc:print": "npx tsc --showConfig"
25 | },
26 | "engines": {
27 | "node": ">=20"
28 | },
29 | "packageManager": "yarn@1.22.22",
30 | "dependencies": {
31 | "@emotion/cache": "^11.14.0",
32 | "@emotion/react": "^11.14.0",
33 | "@emotion/styled": "^11.14.0",
34 | "@fontsource-variable/dm-sans": "^5.2.5",
35 | "@fontsource/barlow": "^5.2.5",
36 | "@iconify/react": "^5.2.1",
37 | "@mui/lab": "^7.0.0-beta.10",
38 | "@mui/material": "^7.0.1",
39 | "apexcharts": "^4.5.0",
40 | "dayjs": "^1.11.13",
41 | "es-toolkit": "^1.34.1",
42 | "minimal-shared": "^1.0.7",
43 | "react": "^19.1.0",
44 | "react-apexcharts": "^1.7.0",
45 | "react-dom": "^19.1.0",
46 | "react-router-dom": "^7.4.1",
47 | "simplebar-react": "^3.3.0"
48 | },
49 | "devDependencies": {
50 | "@eslint/js": "^9.23.0",
51 | "@types/node": "^22.14.0",
52 | "@types/react": "^19.1.0",
53 | "@types/react-dom": "^19.1.1",
54 | "@typescript-eslint/parser": "^8.29.0",
55 | "@vitejs/plugin-react-swc": "^3.8.1",
56 | "eslint": "^9.23.0",
57 | "eslint-import-resolver-typescript": "^4.3.1",
58 | "eslint-plugin-import": "^2.31.0",
59 | "eslint-plugin-perfectionist": "^4.11.0",
60 | "eslint-plugin-react": "^7.37.4",
61 | "eslint-plugin-react-hooks": "^5.2.0",
62 | "eslint-plugin-unused-imports": "^4.1.4",
63 | "globals": "^16.0.0",
64 | "prettier": "^3.5.3",
65 | "typescript": "^5.8.2",
66 | "typescript-eslint": "^8.29.0",
67 | "vite": "^6.2.5",
68 | "vite-plugin-checker": "^0.9.1"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/prettier.config.mjs:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {import("prettier").Config}
3 | * Need to restart IDE when changing configuration
4 | * Open the command palette (Ctrl + Shift + P) and execute the command > Reload Window.
5 | */
6 | const config = {
7 | semi: true,
8 | tabWidth: 2,
9 | endOfLine: 'lf',
10 | printWidth: 100,
11 | singleQuote: true,
12 | trailingComma: 'es5',
13 | };
14 |
15 | export default config;
16 |
--------------------------------------------------------------------------------
/public/assets/background/overlay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/background/overlay.jpg
--------------------------------------------------------------------------------
/public/assets/icons/flags/ic-flag-de.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/icons/flags/ic-flag-en.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/icons/flags/ic-flag-fr.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/icons/glass/ic-glass-message.svg:
--------------------------------------------------------------------------------
1 |
31 |
--------------------------------------------------------------------------------
/public/assets/icons/glass/ic-glass-users.svg:
--------------------------------------------------------------------------------
1 |
40 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-analytics.svg:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-blog.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-cart.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-disabled.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-lock.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/public/assets/icons/navbar/ic-user.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/public/assets/icons/notification/ic-notification-chat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/icons/notification/ic-notification-mail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/icons/notification/ic-notification-package.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/icons/notification/ic-notification-shipping.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/icons/shape-avatar.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/assets/icons/workspaces/logo-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/icons/workspaces/logo-1.webp
--------------------------------------------------------------------------------
/public/assets/icons/workspaces/logo-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/icons/workspaces/logo-2.webp
--------------------------------------------------------------------------------
/public/assets/icons/workspaces/logo-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/icons/workspaces/logo-3.webp
--------------------------------------------------------------------------------
/public/assets/illustrations/illustration-404.svg:
--------------------------------------------------------------------------------
1 |
41 |
--------------------------------------------------------------------------------
/public/assets/illustrations/illustration-dashboard.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/illustrations/illustration-dashboard.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-1.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-10.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-10.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-11.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-11.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-12.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-12.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-13.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-13.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-14.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-14.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-15.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-15.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-16.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-16.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-17.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-17.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-18.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-18.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-19.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-19.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-2.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-20.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-20.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-21.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-21.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-22.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-22.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-23.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-23.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-24.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-24.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-25.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-25.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-3.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-4.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-5.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-6.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-7.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-8.webp
--------------------------------------------------------------------------------
/public/assets/images/avatar/avatar-9.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/avatar/avatar-9.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-1.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-10.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-10.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-11.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-11.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-12.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-12.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-13.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-13.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-14.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-14.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-15.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-15.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-16.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-16.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-17.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-17.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-18.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-18.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-19.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-19.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-2.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-20.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-20.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-21.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-21.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-22.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-22.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-23.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-23.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-24.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-24.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-3.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-4.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-5.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-6.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-7.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-8.webp
--------------------------------------------------------------------------------
/public/assets/images/cover/cover-9.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/cover/cover-9.webp
--------------------------------------------------------------------------------
/public/assets/images/minimal-free-preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/minimal-free-preview.jpg
--------------------------------------------------------------------------------
/public/assets/images/product/product-1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-1.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-10.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-10.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-11.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-11.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-12.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-12.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-13.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-13.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-14.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-14.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-15.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-15.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-16.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-16.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-17.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-17.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-18.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-18.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-19.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-19.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-2.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-20.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-20.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-21.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-21.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-22.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-22.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-23.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-23.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-24.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-24.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-3.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-4.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-5.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-6.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-7.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-8.webp
--------------------------------------------------------------------------------
/public/assets/images/product/product-9.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/assets/images/product/product-9.webp
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minimal-ui-kit/material-kit-react/69e780a40c455d664b2e13c525faf1d492a072a7/public/favicon.ico
--------------------------------------------------------------------------------
/src/_mock/index.ts:
--------------------------------------------------------------------------------
1 | export * from './_mock';
2 | export * from './_data';
3 |
--------------------------------------------------------------------------------
/src/app.tsx:
--------------------------------------------------------------------------------
1 | import 'src/global.css';
2 |
3 | import { useEffect } from 'react';
4 |
5 | import Fab from '@mui/material/Fab';
6 |
7 | import { usePathname } from 'src/routes/hooks';
8 |
9 | import { ThemeProvider } from 'src/theme/theme-provider';
10 |
11 | import { Iconify } from 'src/components/iconify';
12 |
13 | // ----------------------------------------------------------------------
14 |
15 | type AppProps = {
16 | children: React.ReactNode;
17 | };
18 |
19 | export default function App({ children }: AppProps) {
20 | useScrollToTop();
21 |
22 | const githubButton = () => (
23 |
37 |
38 |
39 | );
40 |
41 | return (
42 |
43 | {children}
44 | {githubButton()}
45 |
46 | );
47 | }
48 |
49 | // ----------------------------------------------------------------------
50 |
51 | function useScrollToTop() {
52 | const pathname = usePathname();
53 |
54 | useEffect(() => {
55 | window.scrollTo(0, 0);
56 | }, [pathname]);
57 |
58 | return null;
59 | }
60 |
--------------------------------------------------------------------------------
/src/components/chart/chart.tsx:
--------------------------------------------------------------------------------
1 | import { lazy, Suspense } from 'react';
2 | import { useIsClient } from 'minimal-shared/hooks';
3 | import { mergeClasses } from 'minimal-shared/utils';
4 |
5 | import { styled } from '@mui/material/styles';
6 |
7 | import { chartClasses } from './classes';
8 | import { ChartLoading } from './components';
9 |
10 | import type { ChartProps } from './types';
11 |
12 | // ----------------------------------------------------------------------
13 |
14 | const LazyChart = lazy(() =>
15 | import('react-apexcharts').then((module) => ({ default: module.default }))
16 | );
17 |
18 | export function Chart({ type, series, options, slotProps, className, sx, ...other }: ChartProps) {
19 | const isClient = useIsClient();
20 |
21 | const renderFallback = () => ;
22 |
23 | return (
24 |
30 | {isClient ? (
31 |
32 |
33 |
34 | ) : (
35 | renderFallback()
36 | )}
37 |
38 | );
39 | }
40 |
41 | // ----------------------------------------------------------------------
42 |
43 | const ChartRoot = styled('div')(({ theme }) => ({
44 | width: '100%',
45 | flexShrink: 0,
46 | position: 'relative',
47 | borderRadius: theme.shape.borderRadius * 1.5,
48 | }));
49 |
--------------------------------------------------------------------------------
/src/components/chart/classes.ts:
--------------------------------------------------------------------------------
1 | import { createClasses } from 'src/theme/create-classes';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export const chartClasses = {
6 | root: createClasses('chart__root'),
7 | loading: createClasses('chart__loading'),
8 | legends: {
9 | root: createClasses('chart__legends__root'),
10 | item: {
11 | wrap: createClasses('chart__legends__item__wrap'),
12 | root: createClasses('chart__legends__item__root'),
13 | dot: createClasses('chart__legends__item__dot'),
14 | icon: createClasses('chart__legends__item__icon'),
15 | label: createClasses('chart__legends__item__label'),
16 | value: createClasses('chart__legends__item__value'),
17 | },
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/src/components/chart/components/chart-legends.tsx:
--------------------------------------------------------------------------------
1 | import { mergeClasses } from 'minimal-shared/utils';
2 |
3 | import { styled } from '@mui/material/styles';
4 |
5 | import { chartClasses } from '../classes';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | export type ChartLegendsProps = React.ComponentProps & {
10 | labels?: string[];
11 | colors?: string[];
12 | values?: string[];
13 | sublabels?: string[];
14 | icons?: React.ReactNode[];
15 | slotProps?: {
16 | wrapper?: React.ComponentProps;
17 | root?: React.ComponentProps;
18 | dot?: React.ComponentProps;
19 | icon?: React.ComponentProps;
20 | value?: React.ComponentProps;
21 | label?: React.ComponentProps;
22 | };
23 | };
24 |
25 | export function ChartLegends({
26 | sx,
27 | className,
28 | slotProps,
29 | icons = [],
30 | values = [],
31 | labels = [],
32 | colors = [],
33 | sublabels = [],
34 | ...other
35 | }: ChartLegendsProps) {
36 | return (
37 |
38 | {labels.map((series, index) => (
39 |
52 |
53 | {icons.length ? (
54 |
55 | {icons[index]}
56 |
57 | ) : (
58 |
59 | )}
60 |
61 |
62 | {series}
63 | {!!sublabels.length && <> {` (${sublabels[index]})`}>}
64 |
65 |
66 |
67 | {values && (
68 |
69 | {values[index]}
70 |
71 | )}
72 |
73 | ))}
74 |
75 | );
76 | }
77 |
78 | // ----------------------------------------------------------------------
79 |
80 | const ListRoot = styled('ul')(({ theme }) => ({
81 | display: 'flex',
82 | flexWrap: 'wrap',
83 | gap: theme.spacing(2),
84 | }));
85 |
86 | const ItemWrap = styled('li')(() => ({
87 | display: 'inline-flex',
88 | flexDirection: 'column',
89 | }));
90 |
91 | const ItemRoot = styled('div')(({ theme }) => ({
92 | gap: 6,
93 | alignItems: 'center',
94 | display: 'inline-flex',
95 | justifyContent: 'flex-start',
96 | fontSize: theme.typography.pxToRem(13),
97 | fontWeight: theme.typography.fontWeightMedium,
98 | }));
99 |
100 | const ItemIcon = styled('span')({
101 | display: 'inline-flex',
102 | color: 'var(--icon-color)',
103 | /**
104 | * As ':first-child' for ssr
105 | * https://github.com/emotion-js/emotion/issues/1105#issuecomment-1126025608
106 | */
107 | '& > :first-of-type:not(style):not(:first-of-type ~ *), & > style + *': { width: 20, height: 20 },
108 | });
109 |
110 | const ItemDot = styled('span')({
111 | width: 12,
112 | height: 12,
113 | flexShrink: 0,
114 | display: 'flex',
115 | borderRadius: '50%',
116 | position: 'relative',
117 | alignItems: 'center',
118 | justifyContent: 'center',
119 | color: 'var(--icon-color)',
120 | backgroundColor: 'currentColor',
121 | });
122 |
123 | const ItemLabel = styled('span')({ flexShrink: 0 });
124 |
125 | const ItemValue = styled('span')(({ theme }) => ({
126 | ...theme.typography.h6,
127 | marginTop: theme.spacing(1),
128 | }));
129 |
--------------------------------------------------------------------------------
/src/components/chart/components/chart-loading.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 |
3 | import { mergeClasses } from 'minimal-shared/utils';
4 |
5 | import Box from '@mui/material/Box';
6 | import Skeleton from '@mui/material/Skeleton';
7 |
8 | import { chartClasses } from '../classes';
9 |
10 | import type { ChartProps } from '../types';
11 |
12 | // ----------------------------------------------------------------------
13 |
14 | export type ChartLoadingProps = BoxProps & Pick;
15 |
16 | export function ChartLoading({ sx, className, type, ...other }: ChartLoadingProps) {
17 | const circularTypes: ChartProps['type'][] = ['donut', 'radialBar', 'pie', 'polarArea'];
18 |
19 | return (
20 | ({
24 | top: 0,
25 | left: 0,
26 | width: 1,
27 | zIndex: 9,
28 | height: 1,
29 | p: 'inherit',
30 | overflow: 'hidden',
31 | alignItems: 'center',
32 | position: 'absolute',
33 | borderRadius: 'inherit',
34 | justifyContent: 'center',
35 | }),
36 | ...(Array.isArray(sx) ? sx : [sx]),
37 | ]}
38 | {...other}
39 | >
40 |
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/chart/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chart-legends';
2 |
3 | export * from './chart-loading';
4 |
--------------------------------------------------------------------------------
/src/components/chart/index.ts:
--------------------------------------------------------------------------------
1 | export * from './chart';
2 |
3 | export * from './use-chart';
4 |
5 | export * from './components';
6 |
7 | export type * from './types';
8 |
--------------------------------------------------------------------------------
/src/components/chart/styles.css:
--------------------------------------------------------------------------------
1 | .apexcharts-canvas {
2 | /**
3 | * Tooltip
4 | */
5 | .apexcharts-tooltip {
6 | min-width: 80px;
7 | border-radius: 10px;
8 | backdrop-filter: blur(6px);
9 | color: var(--palette-text-primary);
10 | box-shadow: var(--customShadows-dropdown);
11 | background-color: rgba(var(--palette-background-defaultChannel) / 0.9);
12 | }
13 | .apexcharts-xaxistooltip {
14 | border-radius: 10px;
15 | border-color: transparent;
16 | backdrop-filter: blur(6px);
17 | color: var(--palette-text-primary);
18 | box-shadow: var(--customShadows-dropdown);
19 | background-color: rgba(var(--palette-background-defaultChannel) / 0.9);
20 | &::before {
21 | border-bottom-color: rgba(var(--palette-grey-500Channel) / 0.16);
22 | }
23 | &::after {
24 | border-bottom-color: rgba(var(--palette-background-defaultChannel) / 0.9);
25 | }
26 | }
27 | .apexcharts-tooltip-title {
28 | font-weight: 700;
29 | text-align: center;
30 | color: var(--palette-text-secondary);
31 | background-color: var(--palette-background-neutral);
32 | }
33 | /**
34 | * Tooltip: group
35 | */
36 | .apexcharts-tooltip-series-group {
37 | padding: 4px 12px;
38 | }
39 | .apexcharts-tooltip-marker {
40 | margin-right: 8px;
41 | }
42 | /**
43 | * Legend
44 | */
45 | .apexcharts-legend {
46 | padding: 0;
47 | }
48 | .apexcharts-legend-marker {
49 | margin-right: 6px;
50 | }
51 | .apexcharts-legend-text {
52 | margin-left: 0;
53 | padding-left: 0;
54 | line-height: 18px;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/chart/types.ts:
--------------------------------------------------------------------------------
1 | import type { Theme, SxProps } from '@mui/material/styles';
2 | import type { Props as ApexProps } from 'react-apexcharts';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | export type ChartOptions = ApexProps['options'];
7 |
8 | export type ChartProps = React.ComponentProps<'div'> &
9 | Pick & {
10 | sx?: SxProps;
11 | slotProps?: {
12 | loading?: SxProps;
13 | };
14 | };
15 |
--------------------------------------------------------------------------------
/src/components/color-utils/classes.ts:
--------------------------------------------------------------------------------
1 | import { createClasses } from 'src/theme/create-classes';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export const colorPreviewClasses = {
6 | root: createClasses('color__preview__root'),
7 | item: createClasses('color__preview__item'),
8 | label: createClasses('color__preview__label'),
9 | };
10 |
11 | export const colorPickerClasses = {
12 | root: createClasses('color__picker__root'),
13 | item: {
14 | root: createClasses('color__picker__item__root'),
15 | container: createClasses('color__picker__item__container'),
16 | icon: createClasses('color__picker__item__icon'),
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/src/components/color-utils/color-preview.tsx:
--------------------------------------------------------------------------------
1 | import { varAlpha, mergeClasses } from 'minimal-shared/utils';
2 |
3 | import { styled } from '@mui/material/styles';
4 |
5 | import { colorPreviewClasses } from './classes';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | export type ColorPreviewSlotProps = {
10 | item?: React.ComponentProps;
11 | label?: React.ComponentProps;
12 | };
13 |
14 | export type ColorPreviewProps = React.ComponentProps & {
15 | limit?: number;
16 | size?: number;
17 | gap?: number;
18 | colors: string[];
19 | slotProps?: ColorPreviewSlotProps;
20 | };
21 |
22 | export function ColorPreview({
23 | sx,
24 | colors,
25 | className,
26 | slotProps,
27 | gap = 6,
28 | limit = 3,
29 | size = 16,
30 | ...other
31 | }: ColorPreviewProps) {
32 | const colorsRange = colors.slice(0, limit);
33 | const remainingColorCount = colors.length - limit;
34 |
35 | return (
36 |
41 | {colorsRange.map((color, index) => (
42 |
57 | ))}
58 |
59 | {colors.length > limit && (
60 | {`+${remainingColorCount}`}
64 | )}
65 |
66 | );
67 | }
68 |
69 | // ----------------------------------------------------------------------
70 |
71 | const ColorPreviewRoot = styled('ul')(() => ({
72 | display: 'flex',
73 | flexDirection: 'row',
74 | alignItems: 'center',
75 | justifyContent: 'flex-end',
76 | }));
77 |
78 | const ItemRoot = styled('li')(({ theme }) => ({
79 | borderRadius: '50%',
80 | width: 'var(--item-size)',
81 | height: 'var(--item-size)',
82 | marginLeft: 'var(--item-gap)',
83 | backgroundColor: 'var(--item-color)',
84 | border: `solid 2px ${theme.vars.palette.background.paper}`,
85 | boxShadow: `inset -1px 1px 2px ${varAlpha(theme.vars.palette.common.blackChannel, 0.24)}`,
86 | }));
87 |
88 | const ItemLabel = styled('li')(({ theme }) => ({
89 | ...theme.typography.subtitle2,
90 | }));
91 |
--------------------------------------------------------------------------------
/src/components/color-utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './classes';
2 |
3 | export * from './color-picker';
4 |
5 | export * from './color-preview';
6 |
--------------------------------------------------------------------------------
/src/components/iconify/classes.ts:
--------------------------------------------------------------------------------
1 | import { createClasses } from 'src/theme/create-classes';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export const iconifyClasses = {
6 | root: createClasses('iconify__root'),
7 | };
8 |
--------------------------------------------------------------------------------
/src/components/iconify/iconify.tsx:
--------------------------------------------------------------------------------
1 | import type { IconProps } from '@iconify/react';
2 |
3 | import { useId } from 'react';
4 | import { Icon } from '@iconify/react';
5 | import { mergeClasses } from 'minimal-shared/utils';
6 |
7 | import { styled } from '@mui/material/styles';
8 |
9 | import { iconifyClasses } from './classes';
10 | import { allIconNames, registerIcons } from './register-icons';
11 |
12 | import type { IconifyName } from './register-icons';
13 |
14 | // ----------------------------------------------------------------------
15 |
16 | export type IconifyProps = React.ComponentProps &
17 | Omit & {
18 | icon: IconifyName;
19 | };
20 |
21 | export function Iconify({ className, icon, width = 20, height, sx, ...other }: IconifyProps) {
22 | const id = useId();
23 |
24 | if (!allIconNames.includes(icon)) {
25 | console.warn(
26 | [
27 | `Icon "${icon}" is currently loaded online, which may cause flickering effects.`,
28 | `To ensure a smoother experience, please register your icon collection for offline use.`,
29 | `More information is available at: https://docs.minimals.cc/icons/`,
30 | ].join('\n')
31 | );
32 | }
33 |
34 | registerIcons();
35 |
36 | return (
37 |
53 | );
54 | }
55 |
56 | // ----------------------------------------------------------------------
57 |
58 | const IconRoot = styled(Icon)``;
59 |
--------------------------------------------------------------------------------
/src/components/iconify/index.ts:
--------------------------------------------------------------------------------
1 | export * from './classes';
2 |
3 | export * from './iconify';
4 |
5 | export * from './register-icons';
6 |
--------------------------------------------------------------------------------
/src/components/iconify/register-icons.ts:
--------------------------------------------------------------------------------
1 | import type { IconifyJSON } from '@iconify/react';
2 |
3 | import { addCollection } from '@iconify/react';
4 |
5 | import allIcons from './icon-sets';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | export const iconSets = Object.entries(allIcons).reduce((acc, [key, value]) => {
10 | const [prefix, iconName] = key.split(':');
11 | const existingPrefix = acc.find((item) => item.prefix === prefix);
12 |
13 | if (existingPrefix) {
14 | existingPrefix.icons[iconName] = value;
15 | } else {
16 | acc.push({
17 | prefix,
18 | icons: {
19 | [iconName]: value,
20 | },
21 | });
22 | }
23 |
24 | return acc;
25 | }, [] as IconifyJSON[]);
26 |
27 | export const allIconNames = Object.keys(allIcons) as IconifyName[];
28 |
29 | export type IconifyName = keyof typeof allIcons;
30 |
31 | // ----------------------------------------------------------------------
32 |
33 | let areIconsRegistered = false;
34 |
35 | export function registerIcons() {
36 | if (areIconsRegistered) {
37 | return;
38 | }
39 |
40 | iconSets.forEach((iconSet) => {
41 | const iconSetConfig = {
42 | ...iconSet,
43 | width: (iconSet.prefix === 'carbon' && 32) || 24,
44 | height: (iconSet.prefix === 'carbon' && 32) || 24,
45 | };
46 |
47 | addCollection(iconSetConfig);
48 | });
49 |
50 | areIconsRegistered = true;
51 | }
52 |
--------------------------------------------------------------------------------
/src/components/label/classes.ts:
--------------------------------------------------------------------------------
1 | import { createClasses } from 'src/theme/create-classes';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export const labelClasses = {
6 | root: createClasses('label__root'),
7 | icon: createClasses('label__icon'),
8 | };
9 |
--------------------------------------------------------------------------------
/src/components/label/index.ts:
--------------------------------------------------------------------------------
1 | export * from './label';
2 |
3 | export * from './styles';
4 |
5 | export * from './classes';
6 |
7 | export type * from './types';
8 |
--------------------------------------------------------------------------------
/src/components/label/label.tsx:
--------------------------------------------------------------------------------
1 | import { upperFirst } from 'es-toolkit';
2 | import { mergeClasses } from 'minimal-shared/utils';
3 |
4 | import { labelClasses } from './classes';
5 | import { LabelRoot, LabelIcon } from './styles';
6 |
7 | import type { LabelProps } from './types';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | export function Label({
12 | sx,
13 | endIcon,
14 | children,
15 | startIcon,
16 | className,
17 | disabled,
18 | variant = 'soft',
19 | color = 'default',
20 | ...other
21 | }: LabelProps) {
22 | return (
23 |
31 | {startIcon && {startIcon}}
32 |
33 | {typeof children === 'string' ? upperFirst(children) : children}
34 |
35 | {endIcon && {endIcon}}
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/label/styles.tsx:
--------------------------------------------------------------------------------
1 | import type { CSSObject } from '@mui/material/styles';
2 |
3 | import { varAlpha } from 'minimal-shared/utils';
4 |
5 | import { styled } from '@mui/material/styles';
6 |
7 | import type { LabelProps } from './types';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | export const LabelRoot = styled('span', {
12 | shouldForwardProp: (prop: string) => !['color', 'variant', 'disabled', 'sx'].includes(prop),
13 | })(({ color, variant, disabled, theme }) => {
14 | const defaultStyles: CSSObject = {
15 | ...(color === 'default' && {
16 | /**
17 | * @variant filled
18 | */
19 | ...(variant === 'filled' && {
20 | color: theme.vars.palette.common.white,
21 | backgroundColor: theme.vars.palette.text.primary,
22 | ...theme.applyStyles('dark', {
23 | color: theme.vars.palette.grey[800],
24 | }),
25 | }),
26 | /**
27 | * @variant outlined
28 | */
29 | ...(variant === 'outlined' && {
30 | backgroundColor: 'transparent',
31 | color: theme.vars.palette.text.primary,
32 | border: `2px solid ${theme.vars.palette.text.primary}`,
33 | }),
34 | /**
35 | * @variant soft
36 | */
37 | ...(variant === 'soft' && {
38 | color: theme.vars.palette.text.secondary,
39 | backgroundColor: varAlpha(theme.vars.palette.grey['500Channel'], 0.16),
40 | }),
41 | /**
42 | * @variant inverted
43 | */
44 | ...(variant === 'inverted' && {
45 | color: theme.vars.palette.grey[800],
46 | backgroundColor: theme.vars.palette.grey[300],
47 | }),
48 | }),
49 | };
50 |
51 | const colorStyles: CSSObject = {
52 | ...(color &&
53 | color !== 'default' && {
54 | /**
55 | * @variant filled
56 | */
57 | ...(variant === 'filled' && {
58 | color: theme.vars.palette[color].contrastText,
59 | backgroundColor: theme.vars.palette[color].main,
60 | }),
61 | /**
62 | * @variant outlined
63 | */
64 | ...(variant === 'outlined' && {
65 | backgroundColor: 'transparent',
66 | color: theme.vars.palette[color].main,
67 | border: `2px solid ${theme.vars.palette[color].main}`,
68 | }),
69 | /**
70 | * @variant soft
71 | */
72 | ...(variant === 'soft' && {
73 | color: theme.vars.palette[color].dark,
74 | backgroundColor: varAlpha(theme.vars.palette[color].mainChannel, 0.16),
75 | ...theme.applyStyles('dark', {
76 | color: theme.vars.palette[color].light,
77 | }),
78 | }),
79 | /**
80 | * @variant inverted
81 | */
82 | ...(variant === 'inverted' && {
83 | color: theme.vars.palette[color].darker,
84 | backgroundColor: theme.vars.palette[color].lighter,
85 | }),
86 | }),
87 | };
88 |
89 | return {
90 | height: 24,
91 | minWidth: 24,
92 | lineHeight: 0,
93 | flexShrink: 0,
94 | cursor: 'default',
95 | alignItems: 'center',
96 | whiteSpace: 'nowrap',
97 | display: 'inline-flex',
98 | gap: theme.spacing(0.75),
99 | justifyContent: 'center',
100 | padding: theme.spacing(0, 0.75),
101 | fontSize: theme.typography.pxToRem(12),
102 | fontWeight: theme.typography.fontWeightBold,
103 | borderRadius: theme.shape.borderRadius * 0.75,
104 | transition: theme.transitions.create(['all'], { duration: theme.transitions.duration.shorter }),
105 | ...defaultStyles,
106 | ...colorStyles,
107 | ...(disabled && { opacity: 0.48, pointerEvents: 'none' }),
108 | };
109 | });
110 |
111 | export const LabelIcon = styled('span')({
112 | width: 16,
113 | height: 16,
114 | flexShrink: 0,
115 | '& svg, img': { width: '100%', height: '100%', objectFit: 'cover' },
116 | });
117 |
--------------------------------------------------------------------------------
/src/components/label/types.ts:
--------------------------------------------------------------------------------
1 | import type { Theme, SxProps } from '@mui/material/styles';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export type LabelColor =
6 | | 'default'
7 | | 'primary'
8 | | 'secondary'
9 | | 'info'
10 | | 'success'
11 | | 'warning'
12 | | 'error';
13 |
14 | export type LabelVariant = 'filled' | 'outlined' | 'soft' | 'inverted';
15 |
16 | export interface LabelProps extends React.ComponentProps<'span'> {
17 | sx?: SxProps;
18 | disabled?: boolean;
19 | color?: LabelColor;
20 | variant?: LabelVariant;
21 | endIcon?: React.ReactNode;
22 | startIcon?: React.ReactNode;
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/logo/classes.ts:
--------------------------------------------------------------------------------
1 | import { createClasses } from 'src/theme/create-classes';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export const logoClasses = {
6 | root: createClasses('logo__root'),
7 | };
8 |
--------------------------------------------------------------------------------
/src/components/logo/index.ts:
--------------------------------------------------------------------------------
1 | export * from './logo';
2 |
3 | export * from './classes';
4 |
--------------------------------------------------------------------------------
/src/components/scrollbar/classes.ts:
--------------------------------------------------------------------------------
1 | import { createClasses } from 'src/theme/create-classes';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export const scrollbarClasses = {
6 | root: createClasses('scrollbar__root'),
7 | };
8 |
--------------------------------------------------------------------------------
/src/components/scrollbar/index.ts:
--------------------------------------------------------------------------------
1 | export * from './classes';
2 |
3 | export * from './scrollbar';
4 |
5 | export type * from './types';
6 |
--------------------------------------------------------------------------------
/src/components/scrollbar/scrollbar.tsx:
--------------------------------------------------------------------------------
1 | import SimpleBar from 'simplebar-react';
2 | import { mergeClasses } from 'minimal-shared/utils';
3 |
4 | import { styled } from '@mui/material/styles';
5 |
6 | import { scrollbarClasses } from './classes';
7 |
8 | import type { ScrollbarProps } from './types';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | export function Scrollbar({
13 | sx,
14 | ref,
15 | children,
16 | className,
17 | slotProps,
18 | fillContent = true,
19 | ...other
20 | }: ScrollbarProps) {
21 | return (
22 |
37 | {children}
38 |
39 | );
40 | }
41 |
42 | // ----------------------------------------------------------------------
43 |
44 | const ScrollbarRoot = styled(SimpleBar, {
45 | shouldForwardProp: (prop: string) => !['fillContent', 'sx'].includes(prop),
46 | })>(({ fillContent }) => ({
47 | minWidth: 0,
48 | minHeight: 0,
49 | flexGrow: 1,
50 | display: 'flex',
51 | flexDirection: 'column',
52 | ...(fillContent && {
53 | '& .simplebar-content': {
54 | display: 'flex',
55 | flex: '1 1 auto',
56 | minHeight: '100%',
57 | flexDirection: 'column',
58 | },
59 | }),
60 | }));
61 |
--------------------------------------------------------------------------------
/src/components/scrollbar/styles.css:
--------------------------------------------------------------------------------
1 | @import 'simplebar-react/dist/simplebar.min.css';
2 |
3 | .simplebar-scrollbar:before {
4 | background-color: var(--palette-text-disabled);
5 | }
6 | .simplebar-scrollbar.simplebar-visible:before {
7 | opacity: 0.48;
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/scrollbar/types.ts:
--------------------------------------------------------------------------------
1 | import type { Theme, SxProps } from '@mui/material/styles';
2 | import type { Props as SimplebarProps } from 'simplebar-react';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | export type ScrollbarProps = SimplebarProps &
7 | React.ComponentProps<'div'> & {
8 | sx?: SxProps;
9 | fillContent?: boolean;
10 | slotProps?: {
11 | wrapperSx?: SxProps;
12 | contentSx?: SxProps;
13 | contentWrapperSx?: SxProps;
14 | };
15 | };
16 |
--------------------------------------------------------------------------------
/src/components/svg-color/classes.ts:
--------------------------------------------------------------------------------
1 | import { createClasses } from 'src/theme/create-classes';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export const svgColorClasses = {
6 | root: createClasses('svg__color__root'),
7 | };
8 |
--------------------------------------------------------------------------------
/src/components/svg-color/index.ts:
--------------------------------------------------------------------------------
1 | export * from './classes';
2 |
3 | export * from './svg-color';
4 |
5 | export type * from './types';
6 |
--------------------------------------------------------------------------------
/src/components/svg-color/svg-color.tsx:
--------------------------------------------------------------------------------
1 | import { mergeClasses } from 'minimal-shared/utils';
2 |
3 | import { styled } from '@mui/material/styles';
4 |
5 | import { svgColorClasses } from './classes';
6 |
7 | import type { SvgColorProps } from './types';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | export function SvgColor({ src, className, sx, ...other }: SvgColorProps) {
12 | return (
13 |
24 | );
25 | }
26 |
27 | // ----------------------------------------------------------------------
28 |
29 | const SvgRoot = styled('span')(() => ({
30 | width: 24,
31 | height: 24,
32 | flexShrink: 0,
33 | display: 'inline-flex',
34 | backgroundColor: 'currentColor',
35 | }));
36 |
--------------------------------------------------------------------------------
/src/components/svg-color/types.ts:
--------------------------------------------------------------------------------
1 | import type { Theme, SxProps } from '@mui/material/styles';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export type SvgColorProps = React.ComponentProps<'span'> & {
6 | src: string;
7 | sx?: SxProps;
8 | };
9 |
--------------------------------------------------------------------------------
/src/config-global.ts:
--------------------------------------------------------------------------------
1 | import packageJson from '../package.json';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export type ConfigValue = {
6 | appName: string;
7 | appVersion: string;
8 | };
9 |
10 | export const CONFIG: ConfigValue = {
11 | appName: 'Minimal UI',
12 | appVersion: packageJson.version,
13 | };
14 |
--------------------------------------------------------------------------------
/src/global.css:
--------------------------------------------------------------------------------
1 | /** **************************************
2 | * Fonts: app
3 | *************************************** */
4 | @import '@fontsource-variable/dm-sans';
5 |
6 | @import '@fontsource/barlow/400.css';
7 | @import '@fontsource/barlow/500.css';
8 | @import '@fontsource/barlow/600.css';
9 | @import '@fontsource/barlow/700.css';
10 | @import '@fontsource/barlow/800.css';
11 |
12 | /** **************************************
13 | * Plugins
14 | *************************************** */
15 | /* scrollbar */
16 | @import './components/scrollbar/styles.css';
17 |
18 | /* chart */
19 | @import './components/chart/styles.css';
20 |
21 | /** **************************************
22 | * Baseline
23 | *************************************** */
24 | html {
25 | height: 100%;
26 | -webkit-overflow-scrolling: touch;
27 | }
28 | body,
29 | #root,
30 | #root__layout {
31 | display: flex;
32 | flex: 1 1 auto;
33 | min-height: 100%;
34 | flex-direction: column;
35 | }
36 | img {
37 | max-width: 100%;
38 | vertical-align: middle;
39 | }
40 | ul {
41 | margin: 0;
42 | padding: 0;
43 | list-style-type: none;
44 | }
45 | input[type='number'] {
46 | -moz-appearance: textfield;
47 | appearance: none;
48 | }
49 | input[type='number']::-webkit-outer-spin-button {
50 | margin: 0;
51 | -webkit-appearance: none;
52 | }
53 | input[type='number']::-webkit-inner-spin-button {
54 | margin: 0;
55 | -webkit-appearance: none;
56 | }
57 |
--------------------------------------------------------------------------------
/src/layouts/auth/content.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 |
3 | import { mergeClasses } from 'minimal-shared/utils';
4 |
5 | import Box from '@mui/material/Box';
6 |
7 | import { layoutClasses } from '../core/classes';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | export type AuthContentProps = BoxProps;
12 |
13 | export function AuthContent({ sx, children, className, ...other }: AuthContentProps) {
14 | return (
15 | ({
19 | py: 5,
20 | px: 3,
21 | width: 1,
22 | zIndex: 2,
23 | borderRadius: 2,
24 | display: 'flex',
25 | flexDirection: 'column',
26 | maxWidth: 'var(--layout-auth-content-width)',
27 | bgcolor: theme.vars.palette.background.default,
28 | }),
29 | ...(Array.isArray(sx) ? sx : [sx]),
30 | ]}
31 | {...other}
32 | >
33 | {children}
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/src/layouts/auth/index.ts:
--------------------------------------------------------------------------------
1 | export * from './layout';
2 |
3 | export * from './content';
4 |
--------------------------------------------------------------------------------
/src/layouts/components/language-popover.tsx:
--------------------------------------------------------------------------------
1 | import type { IconButtonProps } from '@mui/material/IconButton';
2 |
3 | import { useState, useCallback } from 'react';
4 | import { usePopover } from 'minimal-shared/hooks';
5 |
6 | import Box from '@mui/material/Box';
7 | import Popover from '@mui/material/Popover';
8 | import MenuList from '@mui/material/MenuList';
9 | import IconButton from '@mui/material/IconButton';
10 | import MenuItem, { menuItemClasses } from '@mui/material/MenuItem';
11 |
12 | // ----------------------------------------------------------------------
13 |
14 | export type LanguagePopoverProps = IconButtonProps & {
15 | data?: {
16 | value: string;
17 | label: string;
18 | icon: string;
19 | }[];
20 | };
21 |
22 | export function LanguagePopover({ data = [], sx, ...other }: LanguagePopoverProps) {
23 | const { open, anchorEl, onClose, onOpen } = usePopover();
24 |
25 | const [locale, setLocale] = useState(data[0].value);
26 |
27 | const handleChangeLang = useCallback(
28 | (newLang: string) => {
29 | setLocale(newLang);
30 | onClose();
31 | },
32 | [onClose]
33 | );
34 |
35 | const currentLang = data.find((lang) => lang.value === locale);
36 |
37 | const renderFlag = (label?: string, icon?: string) => (
38 |
44 | );
45 |
46 | const renderMenuList = () => (
47 |
54 |
73 | {data?.map((option) => (
74 |
82 | ))}
83 |
84 |
85 | );
86 |
87 | return (
88 | <>
89 | ({
94 | p: 0,
95 | width: 40,
96 | height: 40,
97 | ...(open && { bgcolor: theme.vars.palette.action.selected }),
98 | }),
99 | ...(Array.isArray(sx) ? sx : [sx]),
100 | ]}
101 | {...other}
102 | >
103 | {renderFlag(currentLang?.label, currentLang?.icon)}
104 |
105 |
106 | {renderMenuList()}
107 | >
108 | );
109 | }
110 |
--------------------------------------------------------------------------------
/src/layouts/components/menu-button.tsx:
--------------------------------------------------------------------------------
1 | import type { IconButtonProps } from '@mui/material/IconButton';
2 |
3 | import IconButton from '@mui/material/IconButton';
4 |
5 | import { Iconify } from 'src/components/iconify';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | export function MenuButton({ sx, ...other }: IconButtonProps) {
10 | return (
11 |
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/layouts/components/nav-upgrade.tsx:
--------------------------------------------------------------------------------
1 | import type { StackProps } from '@mui/material/Stack';
2 |
3 | import Box from '@mui/material/Box';
4 | import Button from '@mui/material/Button';
5 | import Typography from '@mui/material/Typography';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | export function NavUpgrade({ sx, ...other }: StackProps) {
10 | return (
11 |
24 | ({
28 | background: `linear-gradient(to right, ${theme.vars.palette.secondary.main}, ${theme.vars.palette.warning.main})`,
29 | WebkitBackgroundClip: 'text',
30 | WebkitTextFillColor: 'transparent',
31 | backgroundClip: 'text',
32 | textFillColor: 'transparent',
33 | color: 'transparent',
34 | }),
35 | ]}
36 | >
37 | More features?
38 |
39 |
40 |
41 | {`From only `}
42 |
43 | $69
44 |
45 |
46 |
47 |
53 |
54 |
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/src/layouts/components/searchbar.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 |
3 | import { useState, useCallback } from 'react';
4 | import { varAlpha } from 'minimal-shared/utils';
5 |
6 | import Box from '@mui/material/Box';
7 | import Slide from '@mui/material/Slide';
8 | import Input from '@mui/material/Input';
9 | import Button from '@mui/material/Button';
10 | import { useTheme } from '@mui/material/styles';
11 | import IconButton from '@mui/material/IconButton';
12 | import InputAdornment from '@mui/material/InputAdornment';
13 | import ClickAwayListener from '@mui/material/ClickAwayListener';
14 |
15 | import { Iconify } from 'src/components/iconify';
16 |
17 | // ----------------------------------------------------------------------
18 |
19 | export function Searchbar({ sx, ...other }: BoxProps) {
20 | const theme = useTheme();
21 |
22 | const [open, setOpen] = useState(false);
23 |
24 | const handleOpen = useCallback(() => {
25 | setOpen((prev) => !prev);
26 | }, []);
27 |
28 | const handleClose = useCallback(() => {
29 | setOpen(false);
30 | }, []);
31 |
32 | return (
33 |
34 |
35 | {!open && (
36 |
37 |
38 |
39 | )}
40 |
41 |
42 |
64 |
71 |
72 |
73 | }
74 | sx={{ fontWeight: 'fontWeightBold' }}
75 | />
76 |
79 |
80 |
81 |
82 |
83 | );
84 | }
85 |
--------------------------------------------------------------------------------
/src/layouts/core/classes.ts:
--------------------------------------------------------------------------------
1 | import { createClasses } from 'src/theme/create-classes';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export const layoutClasses = {
6 | root: createClasses('layout__root'),
7 | main: createClasses('layout__main'),
8 | header: createClasses('layout__header'),
9 | nav: {
10 | root: createClasses('layout__nav__root'),
11 | mobile: createClasses('layout__nav__mobile'),
12 | vertical: createClasses('layout__nav__vertical'),
13 | horizontal: createClasses('layout__nav__horizontal'),
14 | },
15 | content: createClasses('layout__main__content'),
16 | sidebarContainer: createClasses('layout__sidebar__container'),
17 | };
18 |
--------------------------------------------------------------------------------
/src/layouts/core/css-vars.ts:
--------------------------------------------------------------------------------
1 | import type { Theme } from '@mui/material/styles';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export function layoutSectionVars(theme: Theme) {
6 | return {
7 | '--layout-nav-zIndex': theme.zIndex.drawer + 1,
8 | '--layout-nav-mobile-width': '288px',
9 | '--layout-header-blur': '8px',
10 | '--layout-header-zIndex': theme.zIndex.appBar + 1,
11 | '--layout-header-mobile-height': '64px',
12 | '--layout-header-desktop-height': '72px',
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/src/layouts/core/index.ts:
--------------------------------------------------------------------------------
1 | export * from './classes';
2 |
3 | export * from './css-vars';
4 |
5 | export * from './main-section';
6 |
7 | export * from './layout-section';
8 |
9 | export * from './header-section';
10 |
--------------------------------------------------------------------------------
/src/layouts/core/layout-section.tsx:
--------------------------------------------------------------------------------
1 | import type { Theme, SxProps, CSSObject } from '@mui/material/styles';
2 |
3 | import { mergeClasses } from 'minimal-shared/utils';
4 |
5 | import { styled } from '@mui/material/styles';
6 | import GlobalStyles from '@mui/material/GlobalStyles';
7 |
8 | import { layoutClasses } from './classes';
9 | import { layoutSectionVars } from './css-vars';
10 |
11 | // ----------------------------------------------------------------------
12 |
13 | export type LayoutSectionProps = React.ComponentProps<'div'> & {
14 | sx?: SxProps;
15 | cssVars?: CSSObject;
16 | children?: React.ReactNode;
17 | footerSection?: React.ReactNode;
18 | headerSection?: React.ReactNode;
19 | sidebarSection?: React.ReactNode;
20 | };
21 |
22 | export function LayoutSection({
23 | sx,
24 | cssVars,
25 | children,
26 | footerSection,
27 | headerSection,
28 | sidebarSection,
29 | className,
30 | ...other
31 | }: LayoutSectionProps) {
32 | const inputGlobalStyles = (
33 | ({ body: { ...layoutSectionVars(theme), ...cssVars } })} />
34 | );
35 |
36 | return (
37 | <>
38 | {inputGlobalStyles}
39 |
40 |
46 | {sidebarSection ? (
47 | <>
48 | {sidebarSection}
49 |
50 | {headerSection}
51 | {children}
52 | {footerSection}
53 |
54 | >
55 | ) : (
56 | <>
57 | {headerSection}
58 | {children}
59 | {footerSection}
60 | >
61 | )}
62 |
63 | >
64 | );
65 | }
66 |
67 | // ----------------------------------------------------------------------
68 |
69 | const LayoutRoot = styled('div')``;
70 |
71 | const LayoutSidebarContainer = styled('div')(() => ({
72 | display: 'flex',
73 | flex: '1 1 auto',
74 | flexDirection: 'column',
75 | }));
76 |
--------------------------------------------------------------------------------
/src/layouts/core/main-section.tsx:
--------------------------------------------------------------------------------
1 | import { mergeClasses } from 'minimal-shared/utils';
2 |
3 | import { styled } from '@mui/material/styles';
4 |
5 | import { layoutClasses } from './classes';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | export type MainSectionProps = React.ComponentProps;
10 |
11 | export function MainSection({ children, className, sx, ...other }: MainSectionProps) {
12 | return (
13 |
14 | {children}
15 |
16 | );
17 | }
18 |
19 | // ----------------------------------------------------------------------
20 |
21 | const MainRoot = styled('main')({
22 | display: 'flex',
23 | flex: '1 1 auto',
24 | flexDirection: 'column',
25 | });
26 |
--------------------------------------------------------------------------------
/src/layouts/dashboard/content.tsx:
--------------------------------------------------------------------------------
1 | import type { Breakpoint } from '@mui/material/styles';
2 | import type { ContainerProps } from '@mui/material/Container';
3 |
4 | import { mergeClasses } from 'minimal-shared/utils';
5 |
6 | import Container from '@mui/material/Container';
7 |
8 | import { layoutClasses } from '../core/classes';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | export type DashboardContentProps = ContainerProps & {
13 | layoutQuery?: Breakpoint;
14 | disablePadding?: boolean;
15 | };
16 |
17 | export function DashboardContent({
18 | sx,
19 | children,
20 | className,
21 | disablePadding,
22 | maxWidth = 'lg',
23 | layoutQuery = 'lg',
24 | ...other
25 | }: DashboardContentProps) {
26 | return (
27 | ({
32 | display: 'flex',
33 | flex: '1 1 auto',
34 | flexDirection: 'column',
35 | pt: 'var(--layout-dashboard-content-pt)',
36 | pb: 'var(--layout-dashboard-content-pb)',
37 | [theme.breakpoints.up(layoutQuery)]: {
38 | px: 'var(--layout-dashboard-content-px)',
39 | },
40 | ...(disablePadding && {
41 | p: {
42 | xs: 0,
43 | sm: 0,
44 | md: 0,
45 | lg: 0,
46 | xl: 0,
47 | },
48 | }),
49 | }),
50 | ...(Array.isArray(sx) ? sx : [sx]),
51 | ]}
52 | {...other}
53 | >
54 | {children}
55 |
56 | );
57 | }
58 |
--------------------------------------------------------------------------------
/src/layouts/dashboard/css-vars.ts:
--------------------------------------------------------------------------------
1 | import type { Theme } from '@mui/material/styles';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export function dashboardLayoutVars(theme: Theme) {
6 | return {
7 | '--layout-transition-easing': 'linear',
8 | '--layout-transition-duration': '120ms',
9 | '--layout-nav-vertical-width': '300px',
10 | '--layout-dashboard-content-pt': theme.spacing(1),
11 | '--layout-dashboard-content-pb': theme.spacing(8),
12 | '--layout-dashboard-content-px': theme.spacing(5),
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/src/layouts/dashboard/index.ts:
--------------------------------------------------------------------------------
1 | export * from './layout';
2 |
3 | export * from './content';
4 |
--------------------------------------------------------------------------------
/src/layouts/nav-config-account.tsx:
--------------------------------------------------------------------------------
1 | import { Iconify } from 'src/components/iconify';
2 |
3 | import type { AccountPopoverProps } from './components/account-popover';
4 |
5 | // ----------------------------------------------------------------------
6 |
7 | export const _account: AccountPopoverProps['data'] = [
8 | {
9 | label: 'Home',
10 | href: '/',
11 | icon: ,
12 | },
13 | {
14 | label: 'Profile',
15 | href: '#',
16 | icon: ,
17 | },
18 | {
19 | label: 'Settings',
20 | href: '#',
21 | icon: ,
22 | },
23 | ];
24 |
--------------------------------------------------------------------------------
/src/layouts/nav-config-dashboard.tsx:
--------------------------------------------------------------------------------
1 | import { Label } from 'src/components/label';
2 | import { SvgColor } from 'src/components/svg-color';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | const icon = (name: string) => ;
7 |
8 | export type NavItem = {
9 | title: string;
10 | path: string;
11 | icon: React.ReactNode;
12 | info?: React.ReactNode;
13 | };
14 |
15 | export const navData = [
16 | {
17 | title: 'Dashboard',
18 | path: '/',
19 | icon: icon('ic-analytics'),
20 | },
21 | {
22 | title: 'User',
23 | path: '/user',
24 | icon: icon('ic-user'),
25 | },
26 | {
27 | title: 'Product',
28 | path: '/products',
29 | icon: icon('ic-cart'),
30 | info: (
31 |
34 | ),
35 | },
36 | {
37 | title: 'Blog',
38 | path: '/blog',
39 | icon: icon('ic-blog'),
40 | },
41 | {
42 | title: 'Sign in',
43 | path: '/sign-in',
44 | icon: icon('ic-lock'),
45 | },
46 | {
47 | title: 'Not found',
48 | path: '/404',
49 | icon: icon('ic-disabled'),
50 | },
51 | ];
52 |
--------------------------------------------------------------------------------
/src/layouts/nav-config-workspace.tsx:
--------------------------------------------------------------------------------
1 | import type { WorkspacesPopoverProps } from './components/workspaces-popover';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export const _workspaces: WorkspacesPopoverProps['data'] = [
6 | {
7 | id: 'team-1',
8 | name: 'Team 1',
9 | plan: 'Free',
10 | logo: '/assets/icons/workspaces/logo-1.webp',
11 | },
12 | {
13 | id: 'team-2',
14 | name: 'Team 2',
15 | plan: 'Pro',
16 | logo: '/assets/icons/workspaces/logo-2.webp',
17 | },
18 | {
19 | id: 'team-3',
20 | name: 'Team 3',
21 | plan: 'Pro',
22 | logo: '/assets/icons/workspaces/logo-3.webp',
23 | },
24 | ];
25 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import { createRoot } from 'react-dom/client';
3 | import { Outlet, RouterProvider, createBrowserRouter } from 'react-router';
4 |
5 | import App from './app';
6 | import { routesSection } from './routes/sections';
7 | import { ErrorBoundary } from './routes/components';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | const router = createBrowserRouter([
12 | {
13 | Component: () => (
14 |
15 |
16 |
17 | ),
18 | errorElement: ,
19 | children: routesSection,
20 | },
21 | ]);
22 |
23 | const root = createRoot(document.getElementById('root')!);
24 |
25 | root.render(
26 |
27 |
28 |
29 | );
30 |
--------------------------------------------------------------------------------
/src/pages/blog.tsx:
--------------------------------------------------------------------------------
1 | import { _posts } from 'src/_mock';
2 | import { CONFIG } from 'src/config-global';
3 |
4 | import { BlogView } from 'src/sections/blog/view';
5 |
6 | // ----------------------------------------------------------------------
7 |
8 | export default function Page() {
9 | return (
10 | <>
11 | {`Blog - ${CONFIG.appName}`}
12 |
13 |
14 | >
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/src/pages/dashboard.tsx:
--------------------------------------------------------------------------------
1 | import { CONFIG } from 'src/config-global';
2 |
3 | import { OverviewAnalyticsView as DashboardView } from 'src/sections/overview/view';
4 |
5 | // ----------------------------------------------------------------------
6 |
7 | export default function Page() {
8 | return (
9 | <>
10 | {`Dashboard - ${CONFIG.appName}`}
11 |
15 |
16 |
17 |
18 | >
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/src/pages/page-not-found.tsx:
--------------------------------------------------------------------------------
1 | import { CONFIG } from 'src/config-global';
2 |
3 | import { NotFoundView } from 'src/sections/error';
4 |
5 | // ----------------------------------------------------------------------
6 |
7 | export default function Page() {
8 | return (
9 | <>
10 | {`404 page not found! | Error - ${CONFIG.appName}`}
11 |
12 |
13 | >
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/pages/products.tsx:
--------------------------------------------------------------------------------
1 | import { CONFIG } from 'src/config-global';
2 |
3 | import { ProductsView } from 'src/sections/product/view';
4 |
5 | // ----------------------------------------------------------------------
6 |
7 | export default function Page() {
8 | return (
9 | <>
10 | {`Products - ${CONFIG.appName}`}
11 |
12 |
13 | >
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/pages/sign-in.tsx:
--------------------------------------------------------------------------------
1 | import { CONFIG } from 'src/config-global';
2 |
3 | import { SignInView } from 'src/sections/auth';
4 |
5 | // ----------------------------------------------------------------------
6 |
7 | export default function Page() {
8 | return (
9 | <>
10 | {`Sign in - ${CONFIG.appName}`}
11 |
12 |
13 | >
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/pages/user.tsx:
--------------------------------------------------------------------------------
1 | import { CONFIG } from 'src/config-global';
2 |
3 | import { UserView } from 'src/sections/user/view';
4 |
5 | // ----------------------------------------------------------------------
6 |
7 | export default function Page() {
8 | return (
9 | <>
10 | {`Users - ${CONFIG.appName}`}
11 |
12 |
13 | >
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/routes/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './router-link';
2 |
3 | export * from './error-boundary';
4 |
--------------------------------------------------------------------------------
/src/routes/components/router-link.tsx:
--------------------------------------------------------------------------------
1 | import type { LinkProps } from 'react-router';
2 |
3 | import { Link } from 'react-router';
4 |
5 | // ----------------------------------------------------------------------
6 |
7 | interface RouterLinkProps extends Omit {
8 | href: string;
9 | ref?: React.RefObject;
10 | }
11 |
12 | export function RouterLink({ href, ref, ...other }: RouterLinkProps) {
13 | return ;
14 | }
15 |
--------------------------------------------------------------------------------
/src/routes/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export { useRouter } from './use-router';
2 |
3 | export { usePathname } from './use-pathname';
4 |
--------------------------------------------------------------------------------
/src/routes/hooks/use-pathname.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { useLocation } from 'react-router';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | export function usePathname() {
7 | const { pathname } = useLocation();
8 |
9 | return useMemo(() => pathname, [pathname]);
10 | }
11 |
--------------------------------------------------------------------------------
/src/routes/hooks/use-router.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { useNavigate } from 'react-router';
3 |
4 | // ----------------------------------------------------------------------
5 |
6 | export function useRouter() {
7 | const navigate = useNavigate();
8 |
9 | const router = useMemo(
10 | () => ({
11 | back: () => navigate(-1),
12 | forward: () => navigate(1),
13 | refresh: () => navigate(0),
14 | push: (href: string) => navigate(href),
15 | replace: (href: string) => navigate(href, { replace: true }),
16 | }),
17 | [navigate]
18 | );
19 |
20 | return router;
21 | }
22 |
--------------------------------------------------------------------------------
/src/routes/sections.tsx:
--------------------------------------------------------------------------------
1 | import type { RouteObject } from 'react-router';
2 |
3 | import { lazy, Suspense } from 'react';
4 | import { Outlet } from 'react-router-dom';
5 | import { varAlpha } from 'minimal-shared/utils';
6 |
7 | import Box from '@mui/material/Box';
8 | import LinearProgress, { linearProgressClasses } from '@mui/material/LinearProgress';
9 |
10 | import { AuthLayout } from 'src/layouts/auth';
11 | import { DashboardLayout } from 'src/layouts/dashboard';
12 |
13 | // ----------------------------------------------------------------------
14 |
15 | export const DashboardPage = lazy(() => import('src/pages/dashboard'));
16 | export const BlogPage = lazy(() => import('src/pages/blog'));
17 | export const UserPage = lazy(() => import('src/pages/user'));
18 | export const SignInPage = lazy(() => import('src/pages/sign-in'));
19 | export const ProductsPage = lazy(() => import('src/pages/products'));
20 | export const Page404 = lazy(() => import('src/pages/page-not-found'));
21 |
22 | const renderFallback = () => (
23 |
31 | varAlpha(theme.vars.palette.text.primaryChannel, 0.16),
36 | [`& .${linearProgressClasses.bar}`]: { bgcolor: 'text.primary' },
37 | }}
38 | />
39 |
40 | );
41 |
42 | export const routesSection: RouteObject[] = [
43 | {
44 | element: (
45 |
46 |
47 |
48 |
49 |
50 | ),
51 | children: [
52 | { index: true, element: },
53 | { path: 'user', element: },
54 | { path: 'products', element: },
55 | { path: 'blog', element: },
56 | ],
57 | },
58 | {
59 | path: 'sign-in',
60 | element: (
61 |
62 |
63 |
64 | ),
65 | },
66 | {
67 | path: '404',
68 | element: ,
69 | },
70 | { path: '*', element: },
71 | ];
72 |
--------------------------------------------------------------------------------
/src/sections/auth/index.ts:
--------------------------------------------------------------------------------
1 | export * from './sign-in-view';
2 |
--------------------------------------------------------------------------------
/src/sections/auth/sign-in-view.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useCallback } from 'react';
2 |
3 | import Box from '@mui/material/Box';
4 | import Link from '@mui/material/Link';
5 | import Button from '@mui/material/Button';
6 | import Divider from '@mui/material/Divider';
7 | import TextField from '@mui/material/TextField';
8 | import IconButton from '@mui/material/IconButton';
9 | import Typography from '@mui/material/Typography';
10 | import InputAdornment from '@mui/material/InputAdornment';
11 |
12 | import { useRouter } from 'src/routes/hooks';
13 |
14 | import { Iconify } from 'src/components/iconify';
15 |
16 | // ----------------------------------------------------------------------
17 |
18 | export function SignInView() {
19 | const router = useRouter();
20 |
21 | const [showPassword, setShowPassword] = useState(false);
22 |
23 | const handleSignIn = useCallback(() => {
24 | router.push('/');
25 | }, [router]);
26 |
27 | const renderForm = (
28 |
35 |
45 |
46 |
47 | Forgot password?
48 |
49 |
50 |
61 | setShowPassword(!showPassword)} edge="end">
62 |
63 |
64 |
65 | ),
66 | },
67 | }}
68 | sx={{ mb: 3 }}
69 | />
70 |
71 |
81 |
82 | );
83 |
84 | return (
85 | <>
86 |
95 | Sign in
96 |
102 | Don’t have an account?
103 |
104 | Get started
105 |
106 |
107 |
108 | {renderForm}
109 |
110 |
114 | OR
115 |
116 |
117 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | >
135 | );
136 | }
137 |
--------------------------------------------------------------------------------
/src/sections/blog/post-search.tsx:
--------------------------------------------------------------------------------
1 | import type { Theme, SxProps } from '@mui/material/styles';
2 |
3 | import TextField from '@mui/material/TextField';
4 | import InputAdornment from '@mui/material/InputAdornment';
5 | import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
6 |
7 | import { Iconify } from 'src/components/iconify';
8 |
9 | import type { IPostItem } from './post-item';
10 |
11 | // ----------------------------------------------------------------------
12 |
13 | type PostSearchProps = {
14 | posts: IPostItem[];
15 | sx?: SxProps;
16 | };
17 |
18 | export function PostSearch({ posts, sx }: PostSearchProps) {
19 | return (
20 | post.title}
37 | isOptionEqualToValue={(option, value) => option.id === value.id}
38 | renderInput={(params) => (
39 |
47 |
51 |
52 | ),
53 | },
54 | }}
55 | />
56 | )}
57 | />
58 | );
59 | }
60 |
--------------------------------------------------------------------------------
/src/sections/blog/post-sort.tsx:
--------------------------------------------------------------------------------
1 | import type { ButtonProps } from '@mui/material/Button';
2 |
3 | import { useState, useCallback } from 'react';
4 | import { varAlpha } from 'minimal-shared/utils';
5 |
6 | import Button from '@mui/material/Button';
7 | import Popover from '@mui/material/Popover';
8 | import MenuList from '@mui/material/MenuList';
9 | import MenuItem, { menuItemClasses } from '@mui/material/MenuItem';
10 |
11 | import { Iconify } from 'src/components/iconify';
12 |
13 | // ----------------------------------------------------------------------
14 |
15 | type PostSortProps = ButtonProps & {
16 | sortBy: string;
17 | onSort: (newSort: string) => void;
18 | options: { value: string; label: string }[];
19 | };
20 |
21 | export function PostSort({ options, sortBy, onSort, sx, ...other }: PostSortProps) {
22 | const [openPopover, setOpenPopover] = useState(null);
23 |
24 | const handleOpenPopover = useCallback((event: React.MouseEvent) => {
25 | setOpenPopover(event.currentTarget);
26 | }, []);
27 |
28 | const handleClosePopover = useCallback(() => {
29 | setOpenPopover(null);
30 | }, []);
31 |
32 | return (
33 | <>
34 |
45 | }
46 | sx={[
47 | {
48 | bgcolor: (theme) => varAlpha(theme.vars.palette.grey['500Channel'], 0.08),
49 | },
50 | ...(Array.isArray(sx) ? sx : [sx]),
51 | ]}
52 | {...other}
53 | >
54 | {options.find((option) => option.value === sortBy)?.label}
55 |
56 |
57 |
64 |
80 | {options.map((option) => (
81 |
91 | ))}
92 |
93 |
94 | >
95 | );
96 | }
97 |
--------------------------------------------------------------------------------
/src/sections/blog/view/blog-view.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useCallback } from 'react';
2 |
3 | import Box from '@mui/material/Box';
4 | import Grid from '@mui/material/Grid';
5 | import Button from '@mui/material/Button';
6 | import Typography from '@mui/material/Typography';
7 | import Pagination from '@mui/material/Pagination';
8 |
9 | import { DashboardContent } from 'src/layouts/dashboard';
10 |
11 | import { Iconify } from 'src/components/iconify';
12 |
13 | import { PostItem } from '../post-item';
14 | import { PostSort } from '../post-sort';
15 | import { PostSearch } from '../post-search';
16 |
17 | import type { IPostItem } from '../post-item';
18 |
19 | // ----------------------------------------------------------------------
20 |
21 | type Props = {
22 | posts: IPostItem[];
23 | };
24 |
25 | export function BlogView({ posts }: Props) {
26 | const [sortBy, setSortBy] = useState('latest');
27 |
28 | const handleSort = useCallback((newSort: string) => {
29 | setSortBy(newSort);
30 | }, []);
31 |
32 | return (
33 |
34 |
41 |
42 | Blog
43 |
44 | }
48 | >
49 | New post
50 |
51 |
52 |
53 |
61 |
62 |
71 |
72 |
73 |
74 | {posts.map((post, index) => {
75 | const latestPostLarge = index === 0;
76 | const latestPost = index === 1 || index === 2;
77 |
78 | return (
79 |
87 |
88 |
89 | );
90 | })}
91 |
92 |
93 |
94 |
95 | );
96 | }
97 |
--------------------------------------------------------------------------------
/src/sections/blog/view/index.ts:
--------------------------------------------------------------------------------
1 | export * from './blog-view';
2 |
--------------------------------------------------------------------------------
/src/sections/error/index.ts:
--------------------------------------------------------------------------------
1 | export * from './not-found-view';
2 |
--------------------------------------------------------------------------------
/src/sections/error/not-found-view.tsx:
--------------------------------------------------------------------------------
1 | import Box from '@mui/material/Box';
2 | import Button from '@mui/material/Button';
3 | import Container from '@mui/material/Container';
4 | import Typography from '@mui/material/Typography';
5 |
6 | import { RouterLink } from 'src/routes/components';
7 |
8 | import { Logo } from 'src/components/logo';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | export function NotFoundView() {
13 | return (
14 | <>
15 |
16 |
17 |
27 |
28 | Sorry, page not found!
29 |
30 |
31 |
32 | Sorry, we couldn’t find the page you’re looking for. Perhaps you’ve mistyped the URL? Be
33 | sure to check your spelling.
34 |
35 |
36 |
45 |
46 |
49 |
50 | >
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/src/sections/overview/analytics-conversion-rates.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { ChartOptions } from 'src/components/chart';
3 |
4 | import Card from '@mui/material/Card';
5 | import CardHeader from '@mui/material/CardHeader';
6 | import { useTheme, alpha as hexAlpha } from '@mui/material/styles';
7 |
8 | import { fNumber } from 'src/utils/format-number';
9 |
10 | import { Chart, useChart } from 'src/components/chart';
11 |
12 | // ----------------------------------------------------------------------
13 |
14 | type Props = CardProps & {
15 | title?: string;
16 | subheader?: string;
17 | chart: {
18 | colors?: string[];
19 | categories?: string[];
20 | series: {
21 | name: string;
22 | data: number[];
23 | }[];
24 | options?: ChartOptions;
25 | };
26 | };
27 |
28 | export function AnalyticsConversionRates({ title, subheader, chart, sx, ...other }: Props) {
29 | const theme = useTheme();
30 |
31 | const chartColors = chart.colors ?? [
32 | theme.palette.primary.dark,
33 | hexAlpha(theme.palette.primary.dark, 0.24),
34 | ];
35 |
36 | const chartOptions = useChart({
37 | colors: chartColors,
38 | stroke: { width: 2, colors: ['transparent'] },
39 | tooltip: {
40 | shared: true,
41 | intersect: false,
42 | y: {
43 | formatter: (value: number) => fNumber(value),
44 | title: { formatter: (seriesName: string) => `${seriesName}: ` },
45 | },
46 | },
47 | xaxis: { categories: chart.categories },
48 | dataLabels: {
49 | enabled: true,
50 | offsetX: -6,
51 | style: { fontSize: '10px', colors: ['#FFFFFF', theme.palette.text.primary] },
52 | },
53 | plotOptions: {
54 | bar: {
55 | horizontal: true,
56 | borderRadius: 2,
57 | barHeight: '48%',
58 | dataLabels: { position: 'top' },
59 | },
60 | },
61 | ...chart.options,
62 | });
63 |
64 | return (
65 |
66 |
67 |
68 |
80 |
81 | );
82 | }
83 |
--------------------------------------------------------------------------------
/src/sections/overview/analytics-current-subject.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { ChartOptions } from 'src/components/chart';
3 |
4 | import Card from '@mui/material/Card';
5 | import Divider from '@mui/material/Divider';
6 | import { useTheme } from '@mui/material/styles';
7 | import CardHeader from '@mui/material/CardHeader';
8 |
9 | import { Chart, useChart, ChartLegends } from 'src/components/chart';
10 |
11 | // ----------------------------------------------------------------------
12 |
13 | type Props = CardProps & {
14 | title?: string;
15 | subheader?: string;
16 | chart: {
17 | colors?: string[];
18 | categories: string[];
19 | series: {
20 | name: string;
21 | data: number[];
22 | }[];
23 | options?: ChartOptions;
24 | };
25 | };
26 |
27 | export function AnalyticsCurrentSubject({ title, subheader, chart, sx, ...other }: Props) {
28 | const theme = useTheme();
29 |
30 | const chartColors = chart.colors ?? [
31 | theme.palette.primary.main,
32 | theme.palette.warning.main,
33 | theme.palette.info.main,
34 | ];
35 |
36 | const chartOptions = useChart({
37 | colors: chartColors,
38 | stroke: { width: 2 },
39 | fill: { opacity: 0.48 },
40 | xaxis: {
41 | categories: chart.categories,
42 | labels: { style: { colors: Array.from({ length: 6 }, () => theme.palette.text.secondary) } },
43 | },
44 | ...chart.options,
45 | });
46 |
47 | return (
48 |
49 |
50 |
51 |
63 |
64 |
65 |
66 | item.name)}
68 | colors={chartOptions?.colors}
69 | sx={{ p: 3, justifyContent: 'center' }}
70 | />
71 |
72 | );
73 | }
74 |
--------------------------------------------------------------------------------
/src/sections/overview/analytics-current-visits.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { ChartOptions } from 'src/components/chart';
3 |
4 | import Card from '@mui/material/Card';
5 | import Divider from '@mui/material/Divider';
6 | import { useTheme } from '@mui/material/styles';
7 | import CardHeader from '@mui/material/CardHeader';
8 |
9 | import { fNumber } from 'src/utils/format-number';
10 |
11 | import { Chart, useChart, ChartLegends } from 'src/components/chart';
12 |
13 | // ----------------------------------------------------------------------
14 |
15 | type Props = CardProps & {
16 | title?: string;
17 | subheader?: string;
18 | chart: {
19 | colors?: string[];
20 | series: {
21 | label: string;
22 | value: number;
23 | }[];
24 | options?: ChartOptions;
25 | };
26 | };
27 |
28 | export function AnalyticsCurrentVisits({ title, subheader, chart, sx, ...other }: Props) {
29 | const theme = useTheme();
30 |
31 | const chartSeries = chart.series.map((item) => item.value);
32 |
33 | const chartColors = chart.colors ?? [
34 | theme.palette.primary.main,
35 | theme.palette.warning.light,
36 | theme.palette.info.dark,
37 | theme.palette.error.main,
38 | ];
39 |
40 | const chartOptions = useChart({
41 | chart: { sparkline: { enabled: true } },
42 | colors: chartColors,
43 | labels: chart.series.map((item) => item.label),
44 | stroke: { width: 0 },
45 | dataLabels: { enabled: true, dropShadow: { enabled: false } },
46 | tooltip: {
47 | y: {
48 | formatter: (value: number) => fNumber(value),
49 | title: { formatter: (seriesName: string) => `${seriesName}` },
50 | },
51 | },
52 | plotOptions: { pie: { donut: { labels: { show: false } } } },
53 | ...chart.options,
54 | });
55 |
56 | return (
57 |
58 |
59 |
60 |
71 |
72 |
73 |
74 |
79 |
80 | );
81 | }
82 |
--------------------------------------------------------------------------------
/src/sections/overview/analytics-news.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 | import type { CardProps } from '@mui/material/Card';
3 |
4 | import Box from '@mui/material/Box';
5 | import Link from '@mui/material/Link';
6 | import Card from '@mui/material/Card';
7 | import Button from '@mui/material/Button';
8 | import Avatar from '@mui/material/Avatar';
9 | import CardHeader from '@mui/material/CardHeader';
10 | import ListItemText from '@mui/material/ListItemText';
11 |
12 | import { fToNow } from 'src/utils/format-time';
13 |
14 | import { Iconify } from 'src/components/iconify';
15 | import { Scrollbar } from 'src/components/scrollbar';
16 |
17 | // ----------------------------------------------------------------------
18 |
19 | type Props = CardProps & {
20 | title?: string;
21 | subheader?: string;
22 | list: {
23 | id: string;
24 | title: string;
25 | coverUrl: string;
26 | description: string;
27 | postedAt: string | number | null;
28 | }[];
29 | };
30 |
31 | export function AnalyticsNews({ title, subheader, list, sx, ...other }: Props) {
32 | return (
33 |
34 |
35 |
36 |
37 |
38 | {list.map((item) => (
39 |
40 | ))}
41 |
42 |
43 |
44 |
45 | }
49 | >
50 | View all
51 |
52 |
53 |
54 | );
55 | }
56 |
57 | // ----------------------------------------------------------------------
58 |
59 | type ItemProps = BoxProps & {
60 | item: Props['list'][number];
61 | };
62 |
63 | function Item({ item, sx, ...other }: ItemProps) {
64 | return (
65 | ({
68 | py: 2,
69 | px: 3,
70 | gap: 2,
71 | display: 'flex',
72 | alignItems: 'center',
73 | borderBottom: `dashed 1px ${theme.vars.palette.divider}`,
74 | }),
75 | ...(Array.isArray(sx) ? sx : [sx]),
76 | ]}
77 | {...other}
78 | >
79 |
85 |
86 | {item.title}}
88 | secondary={item.description}
89 | slotProps={{
90 | primary: { noWrap: true },
91 | secondary: {
92 | noWrap: true,
93 | sx: { mt: 0.5 },
94 | },
95 | }}
96 | />
97 |
98 |
99 | {fToNow(item.postedAt)}
100 |
101 |
102 | );
103 | }
104 |
--------------------------------------------------------------------------------
/src/sections/overview/analytics-order-timeline.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { TimelineItemProps } from '@mui/lab/TimelineItem';
3 |
4 | import Card from '@mui/material/Card';
5 | import Timeline from '@mui/lab/Timeline';
6 | import TimelineDot from '@mui/lab/TimelineDot';
7 | import Typography from '@mui/material/Typography';
8 | import CardHeader from '@mui/material/CardHeader';
9 | import TimelineContent from '@mui/lab/TimelineContent';
10 | import TimelineSeparator from '@mui/lab/TimelineSeparator';
11 | import TimelineConnector from '@mui/lab/TimelineConnector';
12 | import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
13 |
14 | import { fDateTime } from 'src/utils/format-time';
15 |
16 | // ----------------------------------------------------------------------
17 |
18 | type Props = CardProps & {
19 | title?: string;
20 | subheader?: string;
21 | list: {
22 | id: string;
23 | type: string;
24 | title: string;
25 | time: string | number | null;
26 | }[];
27 | };
28 |
29 | export function AnalyticsOrderTimeline({ title, subheader, list, sx, ...other }: Props) {
30 | return (
31 |
32 |
33 |
34 |
37 | {list.map((item, index) => (
38 |
39 | ))}
40 |
41 |
42 | );
43 | }
44 |
45 | // ----------------------------------------------------------------------
46 |
47 | type ItemProps = TimelineItemProps & {
48 | lastItem: boolean;
49 | item: Props['list'][number];
50 | };
51 |
52 | function Item({ item, lastItem, ...other }: ItemProps) {
53 | return (
54 |
55 |
56 |
65 | {lastItem ? null : }
66 |
67 |
68 |
69 | {item.title}
70 |
71 |
72 | {fDateTime(item.time)}
73 |
74 |
75 |
76 | );
77 | }
78 |
--------------------------------------------------------------------------------
/src/sections/overview/analytics-traffic-by-site.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 |
3 | import { varAlpha } from 'minimal-shared/utils';
4 |
5 | import Box from '@mui/material/Box';
6 | import Card from '@mui/material/Card';
7 | import CardHeader from '@mui/material/CardHeader';
8 | import Typography from '@mui/material/Typography';
9 |
10 | import { fShortenNumber } from 'src/utils/format-number';
11 |
12 | import { Iconify } from 'src/components/iconify';
13 |
14 | // ----------------------------------------------------------------------
15 |
16 | type Props = CardProps & {
17 | title?: string;
18 | subheader?: string;
19 | list: { value: string; label: string; total: number }[];
20 | };
21 |
22 | export function AnalyticsTrafficBySite({ title, subheader, list, sx, ...other }: Props) {
23 | return (
24 |
25 |
26 |
34 | {list.map((site) => (
35 | ({
38 | py: 2.5,
39 | display: 'flex',
40 | borderRadius: 1.5,
41 | textAlign: 'center',
42 | alignItems: 'center',
43 | flexDirection: 'column',
44 | border: `solid 1px ${varAlpha(theme.vars.palette.grey['500Channel'], 0.12)}`,
45 | })}
46 | >
47 | {site.value === 'twitter' && }
48 | {site.value === 'facebook' && }
49 | {site.value === 'google' && }
50 | {site.value === 'linkedin' && }
51 |
52 |
53 | {fShortenNumber(site.total)}
54 |
55 |
56 |
57 | {site.label}
58 |
59 |
60 | ))}
61 |
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/src/sections/overview/analytics-website-visits.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { ChartOptions } from 'src/components/chart';
3 |
4 | import Card from '@mui/material/Card';
5 | import CardHeader from '@mui/material/CardHeader';
6 | import { useTheme, alpha as hexAlpha } from '@mui/material/styles';
7 |
8 | import { Chart, useChart } from 'src/components/chart';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type Props = CardProps & {
13 | title?: string;
14 | subheader?: string;
15 | chart: {
16 | colors?: string[];
17 | categories?: string[];
18 | series: {
19 | name: string;
20 | data: number[];
21 | }[];
22 | options?: ChartOptions;
23 | };
24 | };
25 |
26 | export function AnalyticsWebsiteVisits({ title, subheader, chart, sx, ...other }: Props) {
27 | const theme = useTheme();
28 |
29 | const chartColors = chart.colors ?? [
30 | hexAlpha(theme.palette.primary.dark, 0.8),
31 | hexAlpha(theme.palette.warning.main, 0.8),
32 | ];
33 |
34 | const chartOptions = useChart({
35 | colors: chartColors,
36 | stroke: { width: 2, colors: ['transparent'] },
37 | xaxis: { categories: chart.categories },
38 | legend: { show: true },
39 | tooltip: { y: { formatter: (value: number) => `${value} visits` } },
40 | ...chart.options,
41 | });
42 |
43 | return (
44 |
45 |
46 |
47 |
59 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/src/sections/overview/analytics-widget-summary.tsx:
--------------------------------------------------------------------------------
1 | import type { CardProps } from '@mui/material/Card';
2 | import type { PaletteColorKey } from 'src/theme/core';
3 | import type { ChartOptions } from 'src/components/chart';
4 |
5 | import { varAlpha } from 'minimal-shared/utils';
6 |
7 | import Box from '@mui/material/Box';
8 | import Card from '@mui/material/Card';
9 | import { useTheme } from '@mui/material/styles';
10 |
11 | import { fNumber, fPercent, fShortenNumber } from 'src/utils/format-number';
12 |
13 | import { Iconify } from 'src/components/iconify';
14 | import { SvgColor } from 'src/components/svg-color';
15 | import { Chart, useChart } from 'src/components/chart';
16 |
17 | // ----------------------------------------------------------------------
18 |
19 | type Props = CardProps & {
20 | title: string;
21 | total: number;
22 | percent: number;
23 | color?: PaletteColorKey;
24 | icon: React.ReactNode;
25 | chart: {
26 | series: number[];
27 | categories: string[];
28 | options?: ChartOptions;
29 | };
30 | };
31 |
32 | export function AnalyticsWidgetSummary({
33 | sx,
34 | icon,
35 | title,
36 | total,
37 | chart,
38 | percent,
39 | color = 'primary',
40 | ...other
41 | }: Props) {
42 | const theme = useTheme();
43 |
44 | const chartColors = [theme.palette[color].dark];
45 |
46 | const chartOptions = useChart({
47 | chart: { sparkline: { enabled: true } },
48 | colors: chartColors,
49 | xaxis: { categories: chart.categories },
50 | grid: {
51 | padding: {
52 | top: 6,
53 | left: 6,
54 | right: 6,
55 | bottom: 6,
56 | },
57 | },
58 | tooltip: {
59 | y: { formatter: (value: number) => fNumber(value), title: { formatter: () => '' } },
60 | },
61 | markers: {
62 | strokeWidth: 0,
63 | },
64 | ...chart.options,
65 | });
66 |
67 | const renderTrending = () => (
68 |
78 |
79 |
80 | {percent > 0 && '+'}
81 | {fPercent(percent)}
82 |
83 |
84 | );
85 |
86 | return (
87 | ({
90 | p: 3,
91 | boxShadow: 'none',
92 | position: 'relative',
93 | color: `${color}.darker`,
94 | backgroundColor: 'common.white',
95 | backgroundImage: `linear-gradient(135deg, ${varAlpha(theme.vars.palette[color].lighterChannel, 0.48)}, ${varAlpha(theme.vars.palette[color].lightChannel, 0.48)})`,
96 | }),
97 | ...(Array.isArray(sx) ? sx : [sx]),
98 | ]}
99 | {...other}
100 | >
101 | {icon}
102 |
103 | {renderTrending()}
104 |
105 |
113 |
114 | {title}
115 |
116 | {fShortenNumber(total)}
117 |
118 |
119 |
125 |
126 |
127 |
140 |
141 | );
142 | }
143 |
--------------------------------------------------------------------------------
/src/sections/overview/view/index.ts:
--------------------------------------------------------------------------------
1 | export * from './overview-analytics-view';
2 |
--------------------------------------------------------------------------------
/src/sections/product/product-cart-widget.tsx:
--------------------------------------------------------------------------------
1 | import type { BoxProps } from '@mui/material/Box';
2 |
3 | import Box from '@mui/material/Box';
4 | import Badge from '@mui/material/Badge';
5 |
6 | import { RouterLink } from 'src/routes/components';
7 |
8 | import { Iconify } from 'src/components/iconify';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type CartIconProps = BoxProps & {
13 | totalItems: number;
14 | };
15 |
16 | export function CartIcon({ totalItems, sx, ...other }: CartIconProps) {
17 | return (
18 | ({
23 | right: 0,
24 | top: 112,
25 | zIndex: 999,
26 | display: 'flex',
27 | cursor: 'pointer',
28 | position: 'fixed',
29 | color: 'text.primary',
30 | borderTopLeftRadius: 16,
31 | borderBottomLeftRadius: 16,
32 | bgcolor: 'background.paper',
33 | padding: theme.spacing(1, 3, 1, 2),
34 | boxShadow: theme.vars.customShadows.dropdown,
35 | transition: theme.transitions.create(['opacity']),
36 | '&:hover': { opacity: 0.72 },
37 | }),
38 | ...(Array.isArray(sx) ? sx : [sx]),
39 | ]}
40 | {...other}
41 | >
42 |
43 |
44 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/src/sections/product/product-item.tsx:
--------------------------------------------------------------------------------
1 | import Box from '@mui/material/Box';
2 | import Link from '@mui/material/Link';
3 | import Card from '@mui/material/Card';
4 | import Stack from '@mui/material/Stack';
5 | import Typography from '@mui/material/Typography';
6 |
7 | import { fCurrency } from 'src/utils/format-number';
8 |
9 | import { Label } from 'src/components/label';
10 | import { ColorPreview } from 'src/components/color-utils';
11 |
12 | // ----------------------------------------------------------------------
13 |
14 | export type ProductItemProps = {
15 | id: string;
16 | name: string;
17 | price: number;
18 | status: string;
19 | coverUrl: string;
20 | colors: string[];
21 | priceSale: number | null;
22 | };
23 |
24 | export function ProductItem({ product }: { product: ProductItemProps }) {
25 | const renderStatus = (
26 |
39 | );
40 |
41 | const renderImg = (
42 |
54 | );
55 |
56 | const renderPrice = (
57 |
58 |
66 | {product.priceSale && fCurrency(product.priceSale)}
67 |
68 |
69 | {fCurrency(product.price)}
70 |
71 | );
72 |
73 | return (
74 |
75 |
76 | {product.status && renderStatus}
77 | {renderImg}
78 |
79 |
80 |
81 |
82 | {product.name}
83 |
84 |
85 |
92 |
93 | {renderPrice}
94 |
95 |
96 |
97 | );
98 | }
99 |
--------------------------------------------------------------------------------
/src/sections/product/product-sort.tsx:
--------------------------------------------------------------------------------
1 | import type { ButtonProps } from '@mui/material/Button';
2 |
3 | import { useState, useCallback } from 'react';
4 |
5 | import Button from '@mui/material/Button';
6 | import Popover from '@mui/material/Popover';
7 | import MenuList from '@mui/material/MenuList';
8 | import Typography from '@mui/material/Typography';
9 | import MenuItem, { menuItemClasses } from '@mui/material/MenuItem';
10 |
11 | import { Iconify } from 'src/components/iconify';
12 |
13 | // ----------------------------------------------------------------------
14 |
15 | type ProductSortProps = ButtonProps & {
16 | sortBy: string;
17 | onSort: (newSort: string) => void;
18 | options: { value: string; label: string }[];
19 | };
20 |
21 | export function ProductSort({ options, sortBy, onSort, sx, ...other }: ProductSortProps) {
22 | const [openPopover, setOpenPopover] = useState(null);
23 |
24 | const handleOpenPopover = useCallback((event: React.MouseEvent) => {
25 | setOpenPopover(event.currentTarget);
26 | }, []);
27 |
28 | const handleClosePopover = useCallback(() => {
29 | setOpenPopover(null);
30 | }, []);
31 |
32 | return (
33 | <>
34 |
42 | }
43 | sx={sx}
44 | {...other}
45 | >
46 | Sort By:
47 |
48 | {options.find((option) => option.value === sortBy)?.label}
49 |
50 |
51 |
52 |
59 |
75 | {options.map((option) => (
76 |
86 | ))}
87 |
88 |
89 | >
90 | );
91 | }
92 |
--------------------------------------------------------------------------------
/src/sections/product/view/index.ts:
--------------------------------------------------------------------------------
1 | export * from './products-view';
2 |
--------------------------------------------------------------------------------
/src/sections/user/table-empty-rows.tsx:
--------------------------------------------------------------------------------
1 | import type { TableRowProps } from '@mui/material/TableRow';
2 |
3 | import TableRow from '@mui/material/TableRow';
4 | import TableCell from '@mui/material/TableCell';
5 |
6 | // ----------------------------------------------------------------------
7 |
8 | type TableEmptyRowsProps = TableRowProps & {
9 | emptyRows: number;
10 | height?: number;
11 | };
12 |
13 | export function TableEmptyRows({ emptyRows, height, sx, ...other }: TableEmptyRowsProps) {
14 | if (!emptyRows) {
15 | return null;
16 | }
17 |
18 | return (
19 |
23 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/src/sections/user/table-no-data.tsx:
--------------------------------------------------------------------------------
1 | import type { TableRowProps } from '@mui/material/TableRow';
2 |
3 | import Box from '@mui/material/Box';
4 | import TableRow from '@mui/material/TableRow';
5 | import TableCell from '@mui/material/TableCell';
6 | import Typography from '@mui/material/Typography';
7 |
8 | // ----------------------------------------------------------------------
9 |
10 | type TableNoDataProps = TableRowProps & {
11 | searchQuery: string;
12 | };
13 |
14 | export function TableNoData({ searchQuery, ...other }: TableNoDataProps) {
15 | return (
16 |
17 |
18 |
19 |
20 | Not found
21 |
22 |
23 |
24 | No results found for
25 | "{searchQuery}".
26 |
Try checking for typos or using complete words.
27 |
28 |
29 |
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/src/sections/user/user-table-head.tsx:
--------------------------------------------------------------------------------
1 | import Box from '@mui/material/Box';
2 | import TableRow from '@mui/material/TableRow';
3 | import Checkbox from '@mui/material/Checkbox';
4 | import TableHead from '@mui/material/TableHead';
5 | import TableCell from '@mui/material/TableCell';
6 | import TableSortLabel from '@mui/material/TableSortLabel';
7 |
8 | import { visuallyHidden } from './utils';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type UserTableHeadProps = {
13 | orderBy: string;
14 | rowCount: number;
15 | numSelected: number;
16 | order: 'asc' | 'desc';
17 | onSort: (id: string) => void;
18 | headLabel: Record[];
19 | onSelectAllRows: (checked: boolean) => void;
20 | };
21 |
22 | export function UserTableHead({
23 | order,
24 | onSort,
25 | orderBy,
26 | rowCount,
27 | headLabel,
28 | numSelected,
29 | onSelectAllRows,
30 | }: UserTableHeadProps) {
31 | return (
32 |
33 |
34 |
35 | 0 && numSelected < rowCount}
37 | checked={rowCount > 0 && numSelected === rowCount}
38 | onChange={(event: React.ChangeEvent) =>
39 | onSelectAllRows(event.target.checked)
40 | }
41 | />
42 |
43 |
44 | {headLabel.map((headCell) => (
45 |
51 | onSort(headCell.id)}
56 | >
57 | {headCell.label}
58 | {orderBy === headCell.id ? (
59 |
60 | {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
61 |
62 | ) : null}
63 |
64 |
65 | ))}
66 |
67 |
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/src/sections/user/user-table-row.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useCallback } from 'react';
2 |
3 | import Box from '@mui/material/Box';
4 | import Avatar from '@mui/material/Avatar';
5 | import Popover from '@mui/material/Popover';
6 | import TableRow from '@mui/material/TableRow';
7 | import Checkbox from '@mui/material/Checkbox';
8 | import MenuList from '@mui/material/MenuList';
9 | import TableCell from '@mui/material/TableCell';
10 | import IconButton from '@mui/material/IconButton';
11 | import MenuItem, { menuItemClasses } from '@mui/material/MenuItem';
12 |
13 | import { Label } from 'src/components/label';
14 | import { Iconify } from 'src/components/iconify';
15 |
16 | // ----------------------------------------------------------------------
17 |
18 | export type UserProps = {
19 | id: string;
20 | name: string;
21 | role: string;
22 | status: string;
23 | company: string;
24 | avatarUrl: string;
25 | isVerified: boolean;
26 | };
27 |
28 | type UserTableRowProps = {
29 | row: UserProps;
30 | selected: boolean;
31 | onSelectRow: () => void;
32 | };
33 |
34 | export function UserTableRow({ row, selected, onSelectRow }: UserTableRowProps) {
35 | const [openPopover, setOpenPopover] = useState(null);
36 |
37 | const handleOpenPopover = useCallback((event: React.MouseEvent) => {
38 | setOpenPopover(event.currentTarget);
39 | }, []);
40 |
41 | const handleClosePopover = useCallback(() => {
42 | setOpenPopover(null);
43 | }, []);
44 |
45 | return (
46 | <>
47 |
48 |
49 |
50 |
51 |
52 |
53 |
60 |
61 | {row.name}
62 |
63 |
64 |
65 | {row.company}
66 |
67 | {row.role}
68 |
69 |
70 | {row.isVerified ? (
71 |
72 | ) : (
73 | '-'
74 | )}
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
95 |
111 |
115 |
116 |
120 |
121 |
122 | >
123 | );
124 | }
125 |
--------------------------------------------------------------------------------
/src/sections/user/user-table-toolbar.tsx:
--------------------------------------------------------------------------------
1 | import Tooltip from '@mui/material/Tooltip';
2 | import Toolbar from '@mui/material/Toolbar';
3 | import Typography from '@mui/material/Typography';
4 | import IconButton from '@mui/material/IconButton';
5 | import OutlinedInput from '@mui/material/OutlinedInput';
6 | import InputAdornment from '@mui/material/InputAdornment';
7 |
8 | import { Iconify } from 'src/components/iconify';
9 |
10 | // ----------------------------------------------------------------------
11 |
12 | type UserTableToolbarProps = {
13 | numSelected: number;
14 | filterName: string;
15 | onFilterName: (event: React.ChangeEvent) => void;
16 | };
17 |
18 | export function UserTableToolbar({ numSelected, filterName, onFilterName }: UserTableToolbarProps) {
19 | return (
20 | theme.spacing(0, 1, 0, 3),
26 | ...(numSelected > 0 && {
27 | color: 'primary.main',
28 | bgcolor: 'primary.lighter',
29 | }),
30 | }}
31 | >
32 | {numSelected > 0 ? (
33 |
34 | {numSelected} selected
35 |
36 | ) : (
37 |
44 |
45 |
46 | }
47 | sx={{ maxWidth: 320 }}
48 | />
49 | )}
50 |
51 | {numSelected > 0 ? (
52 |
53 |
54 |
55 |
56 |
57 | ) : (
58 |
59 |
60 |
61 |
62 |
63 | )}
64 |
65 | );
66 | }
67 |
--------------------------------------------------------------------------------
/src/sections/user/utils.ts:
--------------------------------------------------------------------------------
1 | import type { UserProps } from './user-table-row';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export const visuallyHidden = {
6 | border: 0,
7 | margin: -1,
8 | padding: 0,
9 | width: '1px',
10 | height: '1px',
11 | overflow: 'hidden',
12 | position: 'absolute',
13 | whiteSpace: 'nowrap',
14 | clip: 'rect(0 0 0 0)',
15 | } as const;
16 |
17 | // ----------------------------------------------------------------------
18 |
19 | export function emptyRows(page: number, rowsPerPage: number, arrayLength: number) {
20 | return page ? Math.max(0, (1 + page) * rowsPerPage - arrayLength) : 0;
21 | }
22 |
23 | // ----------------------------------------------------------------------
24 |
25 | function descendingComparator(a: T, b: T, orderBy: keyof T) {
26 | if (b[orderBy] < a[orderBy]) {
27 | return -1;
28 | }
29 | if (b[orderBy] > a[orderBy]) {
30 | return 1;
31 | }
32 | return 0;
33 | }
34 |
35 | // ----------------------------------------------------------------------
36 |
37 | export function getComparator(
38 | order: 'asc' | 'desc',
39 | orderBy: Key
40 | ): (
41 | a: {
42 | [key in Key]: number | string;
43 | },
44 | b: {
45 | [key in Key]: number | string;
46 | }
47 | ) => number {
48 | return order === 'desc'
49 | ? (a, b) => descendingComparator(a, b, orderBy)
50 | : (a, b) => -descendingComparator(a, b, orderBy);
51 | }
52 |
53 | // ----------------------------------------------------------------------
54 |
55 | type ApplyFilterProps = {
56 | inputData: UserProps[];
57 | filterName: string;
58 | comparator: (a: any, b: any) => number;
59 | };
60 |
61 | export function applyFilter({ inputData, comparator, filterName }: ApplyFilterProps) {
62 | const stabilizedThis = inputData.map((el, index) => [el, index] as const);
63 |
64 | stabilizedThis.sort((a, b) => {
65 | const order = comparator(a[0], b[0]);
66 | if (order !== 0) return order;
67 | return a[1] - b[1];
68 | });
69 |
70 | inputData = stabilizedThis.map((el) => el[0]);
71 |
72 | if (filterName) {
73 | inputData = inputData.filter(
74 | (user) => user.name.toLowerCase().indexOf(filterName.toLowerCase()) !== -1
75 | );
76 | }
77 |
78 | return inputData;
79 | }
80 |
--------------------------------------------------------------------------------
/src/sections/user/view/index.ts:
--------------------------------------------------------------------------------
1 | export * from './user-view';
2 |
--------------------------------------------------------------------------------
/src/theme/core/custom-shadows.ts:
--------------------------------------------------------------------------------
1 | import { varAlpha } from 'minimal-shared/utils';
2 |
3 | import { grey, info, error, common, primary, success, warning, secondary } from './palette';
4 |
5 | import type { ThemeColorScheme } from '../types';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | /**
10 | * TypeScript (type definition and extension)
11 | * @to {@link file://./../extend-theme-types.d.ts}
12 | */
13 |
14 | export interface CustomShadows {
15 | z1?: string;
16 | z4?: string;
17 | z8?: string;
18 | z12?: string;
19 | z16?: string;
20 | z20?: string;
21 | z24?: string;
22 | primary?: string;
23 | secondary?: string;
24 | info?: string;
25 | success?: string;
26 | warning?: string;
27 | error?: string;
28 | card?: string;
29 | dialog?: string;
30 | dropdown?: string;
31 | }
32 |
33 | // ----------------------------------------------------------------------
34 |
35 | export function createShadowColor(colorChannel: string): string {
36 | return `0 8px 16px 0 ${varAlpha(colorChannel, 0.24)}`;
37 | }
38 |
39 | function createCustomShadows(colorChannel: string): CustomShadows {
40 | return {
41 | z1: `0 1px 2px 0 ${varAlpha(colorChannel, 0.16)}`,
42 | z4: `0 4px 8px 0 ${varAlpha(colorChannel, 0.16)}`,
43 | z8: `0 8px 16px 0 ${varAlpha(colorChannel, 0.16)}`,
44 | z12: `0 12px 24px -4px ${varAlpha(colorChannel, 0.16)}`,
45 | z16: `0 16px 32px -4px ${varAlpha(colorChannel, 0.16)}`,
46 | z20: `0 20px 40px -4px ${varAlpha(colorChannel, 0.16)}`,
47 | z24: `0 24px 48px 0 ${varAlpha(colorChannel, 0.16)}`,
48 | /********/
49 | dialog: `-40px 40px 80px -8px ${varAlpha(common.blackChannel, 0.24)}`,
50 | card: `0 0 2px 0 ${varAlpha(colorChannel, 0.2)}, 0 12px 24px -4px ${varAlpha(colorChannel, 0.12)}`,
51 | dropdown: `0 0 2px 0 ${varAlpha(colorChannel, 0.24)}, -20px 20px 40px -4px ${varAlpha(colorChannel, 0.24)}`,
52 | /********/
53 | primary: createShadowColor(primary.mainChannel),
54 | secondary: createShadowColor(secondary.mainChannel),
55 | info: createShadowColor(info.mainChannel),
56 | success: createShadowColor(success.mainChannel),
57 | warning: createShadowColor(warning.mainChannel),
58 | error: createShadowColor(error.mainChannel),
59 | };
60 | }
61 |
62 | export const customShadows: Partial> = {
63 | light: createCustomShadows(grey['500Channel']),
64 | };
65 |
--------------------------------------------------------------------------------
/src/theme/core/index.ts:
--------------------------------------------------------------------------------
1 | export * from './shadows';
2 |
3 | export * from './palette';
4 |
5 | export * from './typography';
6 |
7 | export * from './components';
8 |
9 | export * from './custom-shadows';
10 |
--------------------------------------------------------------------------------
/src/theme/core/shadows.ts:
--------------------------------------------------------------------------------
1 | import type { Shadows } from '@mui/material/styles';
2 |
3 | import { varAlpha } from 'minimal-shared/utils';
4 |
5 | import { grey } from './palette';
6 |
7 | import type { ThemeColorScheme } from '../types';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | function createShadows(colorChannel: string): Shadows {
12 | const color1 = varAlpha(colorChannel, 0.2);
13 | const color2 = varAlpha(colorChannel, 0.14);
14 | const color3 = varAlpha(colorChannel, 0.12);
15 |
16 | return [
17 | 'none',
18 | `0px 2px 1px -1px ${color1},0px 1px 1px 0px ${color2},0px 1px 3px 0px ${color3}`,
19 | `0px 3px 1px -2px ${color1},0px 2px 2px 0px ${color2},0px 1px 5px 0px ${color3}`,
20 | `0px 3px 3px -2px ${color1},0px 3px 4px 0px ${color2},0px 1px 8px 0px ${color3}`,
21 | `0px 2px 4px -1px ${color1},0px 4px 5px 0px ${color2},0px 1px 10px 0px ${color3}`,
22 | `0px 3px 5px -1px ${color1},0px 5px 8px 0px ${color2},0px 1px 14px 0px ${color3}`,
23 | `0px 3px 5px -1px ${color1},0px 6px 10px 0px ${color2},0px 1px 18px 0px ${color3}`,
24 | `0px 4px 5px -2px ${color1},0px 7px 10px 1px ${color2},0px 2px 16px 1px ${color3}`,
25 | `0px 5px 5px -3px ${color1},0px 8px 10px 1px ${color2},0px 3px 14px 2px ${color3}`,
26 | `0px 5px 6px -3px ${color1},0px 9px 12px 1px ${color2},0px 3px 16px 2px ${color3}`,
27 | `0px 6px 6px -3px ${color1},0px 10px 14px 1px ${color2},0px 4px 18px 3px ${color3}`,
28 | `0px 6px 7px -4px ${color1},0px 11px 15px 1px ${color2},0px 4px 20px 3px ${color3}`,
29 | `0px 7px 8px -4px ${color1},0px 12px 17px 2px ${color2},0px 5px 22px 4px ${color3}`,
30 | `0px 7px 8px -4px ${color1},0px 13px 19px 2px ${color2},0px 5px 24px 4px ${color3}`,
31 | `0px 7px 9px -4px ${color1},0px 14px 21px 2px ${color2},0px 5px 26px 4px ${color3}`,
32 | `0px 8px 9px -5px ${color1},0px 15px 22px 2px ${color2},0px 6px 28px 5px ${color3}`,
33 | `0px 8px 10px -5px ${color1},0px 16px 24px 2px ${color2},0px 6px 30px 5px ${color3}`,
34 | `0px 8px 11px -5px ${color1},0px 17px 26px 2px ${color2},0px 6px 32px 5px ${color3}`,
35 | `0px 9px 11px -5px ${color1},0px 18px 28px 2px ${color2},0px 7px 34px 6px ${color3}`,
36 | `0px 9px 12px -6px ${color1},0px 19px 29px 2px ${color2},0px 7px 36px 6px ${color3}`,
37 | `0px 10px 13px -6px ${color1},0px 20px 31px 3px ${color2},0px 8px 38px 7px ${color3}`,
38 | `0px 10px 13px -6px ${color1},0px 21px 33px 3px ${color2},0px 8px 40px 7px ${color3}`,
39 | `0px 10px 14px -6px ${color1},0px 22px 35px 3px ${color2},0px 8px 42px 7px ${color3}`,
40 | `0px 11px 14px -7px ${color1},0px 23px 36px 3px ${color2},0px 9px 44px 8px ${color3}`,
41 | `0px 11px 15px -7px ${color1},0px 24px 38px 3px ${color2},0px 9px 46px 8px ${color3}`,
42 | ];
43 | }
44 |
45 | export const shadows: Partial> = {
46 | light: createShadows(grey['500Channel']),
47 | };
48 |
--------------------------------------------------------------------------------
/src/theme/core/typography.ts:
--------------------------------------------------------------------------------
1 | import type { CSSObject, Breakpoint, TypographyVariantsOptions } from '@mui/material/styles';
2 |
3 | import { pxToRem, setFont } from 'minimal-shared/utils';
4 |
5 | import { createTheme as getTheme } from '@mui/material/styles';
6 |
7 | import { themeConfig } from '../theme-config';
8 |
9 | // ----------------------------------------------------------------------
10 |
11 | /**
12 | * TypeScript (type definition and extension)
13 | * @to {@link file://./../extend-theme-types.d.ts}
14 | */
15 | export type FontStyleExtend = {
16 | fontWeightSemiBold: CSSObject['fontWeight'];
17 | fontSecondaryFamily: CSSObject['fontFamily'];
18 | };
19 |
20 | export type ResponsiveFontSizesInput = Partial>;
21 | export type ResponsiveFontSizesResult = Record;
22 |
23 | const defaultMuiTheme = getTheme();
24 |
25 | function responsiveFontSizes(obj: ResponsiveFontSizesInput): ResponsiveFontSizesResult {
26 | const breakpoints: Breakpoint[] = defaultMuiTheme.breakpoints.keys;
27 |
28 | return breakpoints.reduce((acc, breakpoint) => {
29 | const value = obj[breakpoint];
30 |
31 | if (value !== undefined && value >= 0) {
32 | acc[defaultMuiTheme.breakpoints.up(breakpoint)] = {
33 | fontSize: pxToRem(value),
34 | };
35 | }
36 |
37 | return acc;
38 | }, {} as ResponsiveFontSizesResult);
39 | }
40 |
41 | // ----------------------------------------------------------------------
42 |
43 | const primaryFont = setFont(themeConfig.fontFamily.primary);
44 | const secondaryFont = setFont(themeConfig.fontFamily.secondary);
45 |
46 | export const typography: TypographyVariantsOptions = {
47 | fontFamily: primaryFont,
48 | fontSecondaryFamily: secondaryFont,
49 | fontWeightLight: '300',
50 | fontWeightRegular: '400',
51 | fontWeightMedium: '500',
52 | fontWeightSemiBold: '600',
53 | fontWeightBold: '700',
54 | h1: {
55 | fontFamily: secondaryFont,
56 | fontWeight: 800,
57 | lineHeight: 80 / 64,
58 | fontSize: pxToRem(40),
59 | ...responsiveFontSizes({ sm: 52, md: 58, lg: 64 }),
60 | },
61 | h2: {
62 | fontFamily: secondaryFont,
63 | fontWeight: 800,
64 | lineHeight: 64 / 48,
65 | fontSize: pxToRem(32),
66 | ...responsiveFontSizes({ sm: 40, md: 44, lg: 48 }),
67 | },
68 | h3: {
69 | fontFamily: secondaryFont,
70 | fontWeight: 700,
71 | lineHeight: 1.5,
72 | fontSize: pxToRem(24),
73 | ...responsiveFontSizes({ sm: 26, md: 30, lg: 32 }),
74 | },
75 | h4: {
76 | fontWeight: 700,
77 | lineHeight: 1.5,
78 | fontSize: pxToRem(20),
79 | ...responsiveFontSizes({ md: 24 }),
80 | },
81 | h5: {
82 | fontWeight: 700,
83 | lineHeight: 1.5,
84 | fontSize: pxToRem(18),
85 | ...responsiveFontSizes({ sm: 19 }),
86 | },
87 | h6: {
88 | fontWeight: 600,
89 | lineHeight: 28 / 18,
90 | fontSize: pxToRem(17),
91 | ...responsiveFontSizes({ sm: 18 }),
92 | },
93 | subtitle1: {
94 | fontWeight: 600,
95 | lineHeight: 1.5,
96 | fontSize: pxToRem(16),
97 | },
98 | subtitle2: {
99 | fontWeight: 600,
100 | lineHeight: 22 / 14,
101 | fontSize: pxToRem(14),
102 | },
103 | body1: {
104 | lineHeight: 1.5,
105 | fontSize: pxToRem(16),
106 | },
107 | body2: {
108 | lineHeight: 22 / 14,
109 | fontSize: pxToRem(14),
110 | },
111 | caption: {
112 | lineHeight: 1.5,
113 | fontSize: pxToRem(12),
114 | },
115 | overline: {
116 | fontWeight: 700,
117 | lineHeight: 1.5,
118 | fontSize: pxToRem(12),
119 | textTransform: 'uppercase',
120 | },
121 | button: {
122 | fontWeight: 700,
123 | lineHeight: 24 / 14,
124 | fontSize: pxToRem(14),
125 | textTransform: 'unset',
126 | },
127 | };
128 |
--------------------------------------------------------------------------------
/src/theme/create-classes.ts:
--------------------------------------------------------------------------------
1 | import { themeConfig } from './theme-config';
2 |
3 | // ----------------------------------------------------------------------
4 |
5 | export function createClasses(className: string): string {
6 | return `${themeConfig.classesPrefix}__${className}`;
7 | }
8 |
--------------------------------------------------------------------------------
/src/theme/create-theme.ts:
--------------------------------------------------------------------------------
1 | import type { Theme } from '@mui/material/styles';
2 |
3 | import { createTheme as createMuiTheme } from '@mui/material/styles';
4 |
5 | import { shadows } from './core/shadows';
6 | import { palette } from './core/palette';
7 | import { themeConfig } from './theme-config';
8 | import { components } from './core/components';
9 | import { typography } from './core/typography';
10 | import { customShadows } from './core/custom-shadows';
11 |
12 | import type { ThemeOptions } from './types';
13 |
14 | // ----------------------------------------------------------------------
15 |
16 | export const baseTheme: ThemeOptions = {
17 | colorSchemes: {
18 | light: {
19 | palette: palette.light,
20 | shadows: shadows.light,
21 | customShadows: customShadows.light,
22 | },
23 | },
24 | components,
25 | typography,
26 | shape: { borderRadius: 8 },
27 | cssVariables: themeConfig.cssVariables,
28 | };
29 |
30 | // ----------------------------------------------------------------------
31 |
32 | type CreateThemeProps = {
33 | themeOverrides?: ThemeOptions;
34 | };
35 |
36 | export function createTheme({ themeOverrides = {} }: CreateThemeProps = {}): Theme {
37 | const theme = createMuiTheme(baseTheme, themeOverrides);
38 |
39 | return theme;
40 | }
41 |
--------------------------------------------------------------------------------
/src/theme/extend-theme-types.d.ts:
--------------------------------------------------------------------------------
1 | import type {} from '@mui/lab/themeAugmentation';
2 | import type {} from '@mui/material/themeCssVarsAugmentation';
3 |
4 | import type { FontStyleExtend } from './core/typography';
5 | import type { CustomShadows } from './core/custom-shadows';
6 | import type {
7 | GreyExtend,
8 | TypeTextExtend,
9 | CommonColorsExtend,
10 | PaletteColorExtend,
11 | TypeBackgroundExtend,
12 | } from './core/palette';
13 |
14 | // ----------------------------------------------------------------------
15 |
16 | /** **************************************
17 | * EXTEND CORE
18 | * Palette, typography, shadows...
19 | *************************************** */
20 |
21 | /**
22 | * Palette
23 | * https://mui.com/customization/palette/
24 | * @from {@link file://./core/palette.ts}
25 | */
26 | declare module '@mui/material/styles' {
27 | // grey
28 | interface Color extends GreyExtend {}
29 | // text
30 | interface TypeText extends TypeTextExtend {}
31 | // black & white
32 | interface CommonColors extends CommonColorsExtend {}
33 | // background
34 | interface TypeBackground extends TypeBackgroundExtend {}
35 | // primary, secondary, info, success, warning, error
36 | interface PaletteColor extends PaletteColorExtend {}
37 | interface SimplePaletteColorOptions extends Partial {}
38 | }
39 |
40 | /**
41 | * Typography
42 | * https://mui.com/customization/typography/
43 | * @from {@link file://./core/typography.ts}
44 | */
45 | declare module '@mui/material/styles' {
46 | interface TypographyVariants extends FontStyleExtend {}
47 | interface TypographyVariantsOptions extends Partial {}
48 | }
49 |
50 | declare module '@mui/material/styles' {
51 | /**
52 | * Custom shadows
53 | * @from {@link file://./core/custom-shadows.ts}
54 | */
55 | interface Theme {
56 | customShadows: CustomShadows;
57 | }
58 | interface ThemeOptions {
59 | customShadows?: CustomShadows;
60 | }
61 | interface ThemeVars {
62 | customShadows: CustomShadows;
63 | typography: Theme['typography'];
64 | transitions: Theme['transitions'];
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/theme/index.ts:
--------------------------------------------------------------------------------
1 | export * from './core';
2 |
3 | export * from './types';
4 |
5 | export * from './theme-config';
6 |
7 | export * from './theme-provider';
8 |
--------------------------------------------------------------------------------
/src/theme/theme-config.ts:
--------------------------------------------------------------------------------
1 | import type { CommonColors } from '@mui/material/styles';
2 |
3 | import type { ThemeCssVariables } from './types';
4 | import type { PaletteColorNoChannels } from './core/palette';
5 |
6 | // ----------------------------------------------------------------------
7 |
8 | type ThemeConfig = {
9 | classesPrefix: string;
10 | cssVariables: ThemeCssVariables;
11 | fontFamily: Record<'primary' | 'secondary', string>;
12 | palette: Record<
13 | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error',
14 | PaletteColorNoChannels
15 | > & {
16 | common: Pick;
17 | grey: Record<
18 | '50' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900',
19 | string
20 | >;
21 | };
22 | };
23 |
24 | export const themeConfig: ThemeConfig = {
25 | /** **************************************
26 | * Base
27 | *************************************** */
28 | classesPrefix: 'minimal',
29 | /** **************************************
30 | * Typography
31 | *************************************** */
32 | fontFamily: {
33 | primary: 'DM Sans Variable',
34 | secondary: 'Barlow',
35 | },
36 | /** **************************************
37 | * Palette
38 | *************************************** */
39 | palette: {
40 | primary: {
41 | lighter: '#D0ECFE',
42 | light: '#73BAFB',
43 | main: '#1877F2',
44 | dark: '#0C44AE',
45 | darker: '#042174',
46 | contrastText: '#FFFFFF',
47 | },
48 | secondary: {
49 | lighter: '#EFD6FF',
50 | light: '#C684FF',
51 | main: '#8E33FF',
52 | dark: '#5119B7',
53 | darker: '#27097A',
54 | contrastText: '#FFFFFF',
55 | },
56 | info: {
57 | lighter: '#CAFDF5',
58 | light: '#61F3F3',
59 | main: '#00B8D9',
60 | dark: '#006C9C',
61 | darker: '#003768',
62 | contrastText: '#FFFFFF',
63 | },
64 | success: {
65 | lighter: '#D3FCD2',
66 | light: '#77ED8B',
67 | main: '#22C55E',
68 | dark: '#118D57',
69 | darker: '#065E49',
70 | contrastText: '#ffffff',
71 | },
72 | warning: {
73 | lighter: '#FFF5CC',
74 | light: '#FFD666',
75 | main: '#FFAB00',
76 | dark: '#B76E00',
77 | darker: '#7A4100',
78 | contrastText: '#1C252E',
79 | },
80 | error: {
81 | lighter: '#FFE9D5',
82 | light: '#FFAC82',
83 | main: '#FF5630',
84 | dark: '#B71D18',
85 | darker: '#7A0916',
86 | contrastText: '#FFFFFF',
87 | },
88 | grey: {
89 | '50': '#FCFDFD',
90 | '100': '#F9FAFB',
91 | '200': '#F4F6F8',
92 | '300': '#DFE3E8',
93 | '400': '#C4CDD5',
94 | '500': '#919EAB',
95 | '600': '#637381',
96 | '700': '#454F5B',
97 | '800': '#1C252E',
98 | '900': '#141A21',
99 | },
100 | common: { black: '#000000', white: '#FFFFFF' },
101 | },
102 | /** **************************************
103 | * Css variables
104 | *************************************** */
105 | cssVariables: {
106 | cssVarPrefix: '',
107 | colorSchemeSelector: 'data-color-scheme',
108 | },
109 | };
110 |
--------------------------------------------------------------------------------
/src/theme/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | import type { ThemeProviderProps as MuiThemeProviderProps } from '@mui/material/styles';
2 |
3 | import CssBaseline from '@mui/material/CssBaseline';
4 | import { ThemeProvider as ThemeVarsProvider } from '@mui/material/styles';
5 |
6 | import { createTheme } from './create-theme';
7 |
8 | import type {} from './extend-theme-types';
9 | import type { ThemeOptions } from './types';
10 |
11 | // ----------------------------------------------------------------------
12 |
13 | export type ThemeProviderProps = Partial & {
14 | themeOverrides?: ThemeOptions;
15 | };
16 |
17 | export function ThemeProvider({ themeOverrides, children, ...other }: ThemeProviderProps) {
18 | const theme = createTheme({
19 | themeOverrides,
20 | });
21 |
22 | return (
23 |
24 |
25 | {children}
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/theme/types.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | Shadows,
3 | ColorSystemOptions,
4 | CssVarsThemeOptions,
5 | SupportedColorScheme,
6 | ThemeOptions as MuiThemeOptions,
7 | } from '@mui/material/styles';
8 |
9 | import type { CustomShadows } from './core/custom-shadows';
10 |
11 | // ----------------------------------------------------------------------
12 |
13 | /**
14 | * Theme options
15 | * Extended type that includes additional properties for color schemes and CSS variables.
16 | *
17 | * @see https://github.com/mui/material-ui/blob/master/packages/mui-material/src/styles/createTheme.ts
18 | */
19 |
20 | export type ThemeColorScheme = SupportedColorScheme;
21 | export type ThemeCssVariables = Pick<
22 | CssVarsThemeOptions,
23 | 'colorSchemeSelector' | 'disableCssColorScheme' | 'cssVarPrefix' | 'shouldSkipGeneratingVar'
24 | >;
25 |
26 | type ColorSchemeOptionsExtended = ColorSystemOptions & {
27 | shadows?: Shadows;
28 | customShadows?: CustomShadows;
29 | };
30 |
31 | export type ThemeOptions = Omit &
32 | Pick & {
33 | colorSchemes?: Partial>;
34 | cssVariables?: ThemeCssVariables;
35 | };
36 |
--------------------------------------------------------------------------------
/src/utils/format-number.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Locales code
3 | * https://gist.github.com/raushankrjha/d1c7e35cf87e69aa8b4208a8171a8416
4 | */
5 |
6 | export type InputNumberValue = string | number | null | undefined;
7 |
8 | type Options = Intl.NumberFormatOptions;
9 |
10 | const DEFAULT_LOCALE = { code: 'en-US', currency: 'USD' };
11 |
12 | function processInput(inputValue: InputNumberValue): number | null {
13 | if (inputValue == null || Number.isNaN(inputValue)) return null;
14 | return Number(inputValue);
15 | }
16 |
17 | // ----------------------------------------------------------------------
18 |
19 | export function fNumber(inputValue: InputNumberValue, options?: Options) {
20 | const locale = DEFAULT_LOCALE;
21 |
22 | const number = processInput(inputValue);
23 | if (number === null) return '';
24 |
25 | const fm = new Intl.NumberFormat(locale.code, {
26 | minimumFractionDigits: 0,
27 | maximumFractionDigits: 2,
28 | ...options,
29 | }).format(number);
30 |
31 | return fm;
32 | }
33 |
34 | // ----------------------------------------------------------------------
35 |
36 | export function fCurrency(inputValue: InputNumberValue, options?: Options) {
37 | const locale = DEFAULT_LOCALE;
38 |
39 | const number = processInput(inputValue);
40 | if (number === null) return '';
41 |
42 | const fm = new Intl.NumberFormat(locale.code, {
43 | style: 'currency',
44 | currency: locale.currency,
45 | minimumFractionDigits: 0,
46 | maximumFractionDigits: 2,
47 | ...options,
48 | }).format(number);
49 |
50 | return fm;
51 | }
52 |
53 | // ----------------------------------------------------------------------
54 |
55 | export function fPercent(inputValue: InputNumberValue, options?: Options) {
56 | const locale = DEFAULT_LOCALE;
57 |
58 | const number = processInput(inputValue);
59 | if (number === null) return '';
60 |
61 | const fm = new Intl.NumberFormat(locale.code, {
62 | style: 'percent',
63 | minimumFractionDigits: 0,
64 | maximumFractionDigits: 1,
65 | ...options,
66 | }).format(number / 100);
67 |
68 | return fm;
69 | }
70 |
71 | // ----------------------------------------------------------------------
72 |
73 | export function fShortenNumber(inputValue: InputNumberValue, options?: Options) {
74 | const locale = DEFAULT_LOCALE;
75 |
76 | const number = processInput(inputValue);
77 | if (number === null) return '';
78 |
79 | const fm = new Intl.NumberFormat(locale.code, {
80 | notation: 'compact',
81 | maximumFractionDigits: 2,
82 | ...options,
83 | }).format(number);
84 |
85 | return fm.replace(/[A-Z]/g, (match) => match.toLowerCase());
86 | }
87 |
--------------------------------------------------------------------------------
/src/utils/format-time.ts:
--------------------------------------------------------------------------------
1 | import type { Dayjs } from 'dayjs';
2 |
3 | import dayjs from 'dayjs';
4 | import duration from 'dayjs/plugin/duration';
5 | import relativeTime from 'dayjs/plugin/relativeTime';
6 |
7 | // ----------------------------------------------------------------------
8 |
9 | /**
10 | * @Docs
11 | * https://day.js.org/docs/en/display/format
12 | */
13 |
14 | /**
15 | * Default timezones
16 | * https://day.js.org/docs/en/timezone/set-default-timezone#docsNav
17 | *
18 | */
19 |
20 | /**
21 | * UTC
22 | * https://day.js.org/docs/en/plugin/utc
23 | * @install
24 | * import utc from 'dayjs/plugin/utc';
25 | * dayjs.extend(utc);
26 | * @usage
27 | * dayjs().utc().format()
28 | *
29 | */
30 |
31 | dayjs.extend(duration);
32 | dayjs.extend(relativeTime);
33 |
34 | // ----------------------------------------------------------------------
35 |
36 | export type DatePickerFormat = Dayjs | Date | string | number | null | undefined;
37 |
38 | export const formatPatterns = {
39 | dateTime: 'DD MMM YYYY h:mm a', // 17 Apr 2022 12:00 am
40 | date: 'DD MMM YYYY', // 17 Apr 2022
41 | time: 'h:mm a', // 12:00 am
42 | split: {
43 | dateTime: 'DD/MM/YYYY h:mm a', // 17/04/2022 12:00 am
44 | date: 'DD/MM/YYYY', // 17/04/2022
45 | },
46 | paramCase: {
47 | dateTime: 'DD-MM-YYYY h:mm a', // 17-04-2022 12:00 am
48 | date: 'DD-MM-YYYY', // 17-04-2022
49 | },
50 | };
51 |
52 | const isValidDate = (date: DatePickerFormat) =>
53 | date !== null && date !== undefined && dayjs(date).isValid();
54 |
55 | // ----------------------------------------------------------------------
56 |
57 | /**
58 | * @output 17 Apr 2022 12:00 am
59 | */
60 | export function fDateTime(date: DatePickerFormat, template?: string): string {
61 | if (!isValidDate(date)) {
62 | return 'Invalid date';
63 | }
64 |
65 | return dayjs(date).format(template ?? formatPatterns.dateTime);
66 | }
67 |
68 | // ----------------------------------------------------------------------
69 |
70 | /**
71 | * @output 17 Apr 2022
72 | */
73 | export function fDate(date: DatePickerFormat, template?: string): string {
74 | if (!isValidDate(date)) {
75 | return 'Invalid date';
76 | }
77 |
78 | return dayjs(date).format(template ?? formatPatterns.date);
79 | }
80 |
81 | // ----------------------------------------------------------------------
82 |
83 | /**
84 | * @output a few seconds, 2 years
85 | */
86 | export function fToNow(date: DatePickerFormat): string {
87 | if (!isValidDate(date)) {
88 | return 'Invalid date';
89 | }
90 |
91 | return dayjs(date).toNow(true);
92 | }
93 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Bundler */
4 | "baseUrl": ".",
5 | "module": "ESNext",
6 | "jsx": "react-jsx",
7 | "allowJs": true,
8 | "resolveJsonModule": true,
9 |
10 | /* Build */
11 | "target": "ES2020",
12 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
13 | "moduleResolution": "bundler",
14 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
15 | "incremental": true,
16 | "skipLibCheck": true,
17 | "esModuleInterop": true,
18 | "isolatedModules": true,
19 |
20 | /* Linting */
21 | "strict": true,
22 | "noEmit": true,
23 | "strictNullChecks": true
24 | },
25 | "include": ["src"],
26 | "exclude": ["node_modules"],
27 | "references": [
28 | {
29 | "path": "./tsconfig.node.json"
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "bundler",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [
3 | {
4 | "source": "/(.*)",
5 | "destination": "/"
6 | }
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import checker from 'vite-plugin-checker';
3 | import { defineConfig } from 'vite';
4 | import react from '@vitejs/plugin-react-swc';
5 |
6 | // ----------------------------------------------------------------------
7 |
8 | const PORT = 3039;
9 |
10 | export default defineConfig({
11 | plugins: [
12 | react(),
13 | checker({
14 | typescript: true,
15 | eslint: {
16 | useFlatConfig: true,
17 | lintCommand: 'eslint "./src/**/*.{js,jsx,ts,tsx}"',
18 | dev: { logLevel: ['error'] },
19 | },
20 | overlay: {
21 | position: 'tl',
22 | initialIsOpen: false,
23 | },
24 | }),
25 | ],
26 | resolve: {
27 | alias: [
28 | {
29 | find: /^src(.+)/,
30 | replacement: path.resolve(process.cwd(), 'src/$1'),
31 | },
32 | ],
33 | },
34 | server: { port: PORT, host: true },
35 | preview: { port: PORT, host: true },
36 | });
37 |
--------------------------------------------------------------------------------