diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index 44730a943..1560b6dea 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -25,12 +25,13 @@ jobs: - name: Use Node.js 24 uses: actions/setup-node@v4 with: - node-version: '24' + node-version: "24" - name: πŸ”¨ Build Project run: | + cd vite corepack enable - yarn set version 4.12.0 + yarn set version 4.14.1 yarn yarn build @@ -39,9 +40,9 @@ jobs: env: SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }} # ARGS: "-rltgoDzvO --delete" - SOURCE: 'dist/' + SOURCE: "dist/" REMOTE_HOST: 145.79.3.173 REMOTE_USER: u965251139 REMOTE_PORT: "65002" TARGET: domains/mantisdashboard.com/public_html/free - EXCLUDE: '/build/, /node_modules/' + EXCLUDE: "/build/, /node_modules/" diff --git a/README.md b/README.md index bb31877f5..31afa4cac 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # Mantis Free React Material UI Dashboard Template [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Get%20Mantis%20Free%20React%20-%20The%20Most%20Beautiful%20Material-UI%20Designed%20Admin%20Dashboard%20Template%20&url=https://mantisdashboard.com/free&via=codedthemes&hashtags=react,materialui,nextjs,webdev,developers,typescript) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Price](https://img.shields.io/badge/price-FREE-0098f7.svg)](https://github.com/codedthemes/mantis-free-react-admin-template/blob/master/LICENSE) -[![GitHub package version](https://img.shields.io/github/package-json/v/codedthemes/mantis-free-react-admin-template)](https://github.com/codedthemes/mantis-free-react-admin-template/) -[![Download ZIP](https://img.shields.io/badge/Download-ZIP-blue?style=flat-square&logo=github)](https://codedthemes.com/item/mantis-free-mui-admin-template/) +[![React: 19](https://img.shields.io/badge/React-19-skyblue.svg)](https://react.dev) +[![Material: 9](https://img.shields.io/badge/Material_UI-9-blue.svg)](https://mui.com/material-ui) +[![Next: 16](https://img.shields.io/badge/NextJS-16-black.svg)](https://nextjs.org) +[![Download ZIP](https://img.shields.io/badge/Download-ZIP-green?style=flat-square&logo=github)](https://codedthemes.com/item/mantis-free-mui-admin-template/) [![Join Discord](https://img.shields.io/badge/Join-Discord-5865F2?style=flat-square&logo=discord&logoColor=white)](https://discord.com/invite/p2E2WhCb6s) Mantis is a free and open source React dashboard template made using the Material UI React component library with aim of flexibility and better customizability. @@ -29,13 +30,11 @@ Mantis has Ant Design principal on top of the Material UI React component librar - [Technology Stack](#technology-stack) - [Mantis Figma UI Kit](#mantis-figma-ui-kit) - [Other Technologies](#other-technologies) -- πŸ’°[Save more with Big Bundle](#save-more-with-big-bundle)πŸ’° -- [More React Dashboard Templates](#more-react-dashboard-templates) +- [More React Dashboard Templates from CodedThemes](#more-react-dashboard-templates-from-codedthemes) - [Issues?](#issues) - [License](#license) -- [Contributor](#contributor) +- [Community and Support](#community-and-support) - [Useful Resources](#useful-resources) -- [Community](#community) - [Follow us](#follow-us) ## Getting Started @@ -74,6 +73,7 @@ Mantis offers everything needed to build an advanced dashboard application. In t - Support React19. - Professional user interface. - Material UI React components(MUI v7). +- Prompt Library for centralized access to prebuilt AI prompts. - Fully responsive, all modern browser supported. - Easy to use code structure - Flexible & high-Performance code @@ -83,17 +83,24 @@ Mantis offers everything needed to build an advanced dashboard application. In t The [Pro version](https://mantisdashboard.com/) of Mantis react template includes features such as TypeScript, apps, authentication methods (i.e. JWT, Auth0, Firebase, AWS, Supabase), advance components, form plugins, layouts, widgets, and more. -| [Mantis Free](https://mantisdashboard.com/free/) | [Mantis](https://mantisdashboard.com/) | +| [Mantis Free](https://mantisdashboard.com/free/) | [Mantis Pro](https://mantisdashboard.com/) | | ---------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | -| **7** Demo pages | **85+** demo pages | +| **7** Demo pages | Demo pages (100+) | +| - | βœ“ [Components](https://mantisdashboard.com/components-overview/autocomplete) (299+) | | - | βœ“ Multi-language | | - | βœ“ Dark/Light Mode πŸŒ“ | | - | βœ“ TypeScript version | +| - | βœ“ Next JS 16 | | - | βœ“ Design files (Figma) | | - | βœ“ Multiple color options | | - | βœ“ RTL | -| - | βœ“ JWT authentications | -| - | βœ“ [More components](https://mantisdashboard.com/components-overview/autocomplete) | +| - | βœ“ Applications (5+) | +| - | βœ“ Form Validation | +| - | βœ“ Layout (11+) | +| - | βœ“ Plugins (9+) | +| - | βœ“ React Table (21+) | +| - | βœ“ ApexChart + MUI Chart | +| - | βœ“ Authentications ( JWT, Auth0, Supabase, AWS, Firebase ) | | βœ“ [MIT License](https://github.com/codedthemes/mantis-free-react-admin-template/blob/master/LICENSE) | βœ“ [Pro License](https://mui.com/store/license/) | ## Documentation @@ -102,11 +109,34 @@ The [Pro version](https://mantisdashboard.com/) of Mantis react template include ## Browser support - +

+ + Chrome + +   + + Edge + +   + + Safari + +   + + Firefox + +   + + Opera + +

## Technology Stack -- Material UI V7 +- [Material UI V9](https://material-ui.com/) +- [React 19.2](https://react.dev/) +- [Next JS 16](https://nextjs.org/) +- [Typescript 6](https://www.typescriptlang.org/) - Built with React Hooks API. - React context API for state management. - SWR. @@ -117,9 +147,9 @@ The [Pro version](https://mantisdashboard.com/) of Mantis react template include ## Mantis Figma UI Kit -| FREE | PRO | -| --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| Figma Free | Figma Pro | +| FREE | PRO | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Figma Free | Figma Pro | ## Other Technologies @@ -129,17 +159,13 @@ The [Pro version](https://mantisdashboard.com/) of Mantis react template include |

| [**Free**](https://codedthemes.com/item/mantis-bootstrap-free-admin-template/) | [**Pro**](https://codedthemes.com/item/mantis-bootstrap-admin-dashboard/) | |

| [**Free**](https://codedthemes.com/item/mantis-free-vuetify-vuejs-admin-template/) | [**Pro**](https://codedthemes.com/item/mantis-vue-admin-template/) | -## Save more with Big Bundle +## More React Dashboard Templates from CodedThemes -[![bundle-image](https://org-public-assets.s3.us-west-2.amazonaws.com/Banners/Bundle+banner.png)](https://links.codedthemes.com/jhFBJ) - -## More React Dashboard Templates - -| Dashboard | FREE | PRO | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -| | [**Free**](https://codedthemes.com/item/berry-mui-free-react-admin-template/) | [**Pro**](https://codedthemes.com/item/berry-material-react-admin-template/) | -| | [**Free**](https://codedthemes.com/item/datta-able-react-free-admin-template/) | [**Pro**](https://codedthemes.com/item/datta-able-react-admin-template/) | -| | [**Free**](https://codedthemes.com/item/gradient-able-reactjs-free-admin-template/) | [**Pro**](https://codedthemes.com/item/gradient-able-reactjs-admin-dashboard/) | +| Dashboard | FREE | PRO | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| | [**Free**](https://github.com/codedthemes/berry-free-react-admin-template) | [**Pro**](https://mui.com/store/items/berry-react-material-admin) | +| | [**Free**](https://codedthemes.com/item/datta-able-react-free-admin-template/) | [**Pro**](https://codedthemes.com/item/datta-able-react-admin-template/) | +| | [**Free**](https://codedthemes.com/item/gradient-able-reactjs-free-admin-template/) | [**Pro**](https://codedthemes.com/item/gradient-able-reactjs-admin-dashboard/) | ## Issues @@ -150,20 +176,11 @@ Please generate a [Github issue](https://github.com/codedthemes/mantis-free-reac - Licensed under [MIT](https://github.com/codedthemes/mantis-free-react-admin-template/blob/master/LICENSE) - Copyright Β© [CodedThemes](https://codedthemes.com/) -## Contributor - -**CodedThemes Team** - -- https://x.com/codedthemes -- https://github.com/codedthemes - -**Rakesh Nakrani** +## Community and Support -- https://x.com/rakesh_nakrani - -**Brijesh Dobariya** - -- https://x.com/dobaria_brijesh +- **GitHub Discussion** - [Ask questions and share ideas](https://github.com/codedthemes/mantis-free-react-admin-template/discussions) +- **X/Twitter** β€” [@codedthemes](https://x.com/codedthemes), [@rakesh_nakrani](https://x.com/rakesh_nakrani) [@dobaria_brijesh](https://x.com/dobaria_brijesh) +- **Join Discord** – [Connect with the community](https://discord.com/invite/dW9cBZMJ) ## Useful Resources @@ -174,16 +191,10 @@ Please generate a [Github issue](https://github.com/codedthemes/mantis-free-reac - [Affiliate Program](https://codedthemes.com/affiliate/) - [Blogs](https://blog.codedthemes.com/) -## Community - -- πŸ‘₯Follow [@codedThemes](https://x.com/codedthemes) -- πŸ”—Join [Discord](https://discord.com/invite/p2E2WhCb6s) -- πŸ””Subscribe to [CodedTheme Blogs](https://blog.codedthemes.com/) - ## Follow Us -- [Twitter](https://twitter.com/codedthemes) 🐦 -- [Dribbble](https://dribbble.com/codedthemes) πŸ€ +- [X/Twitter](https://twitter.com/codedthemes) 🐦 +- [Dribbble](https://l1nq.com/4hxyjrt) πŸ€ - [Github](https://github.com/codedthemes) πŸ™ - [LinkedIn](https://www.linkedin.com/company/codedthemes/) πŸ’Ό - [Instagram](https://www.instagram.com/codedthemes/) πŸ“· diff --git a/index.html b/index.html deleted file mode 100644 index a923646ed..000000000 --- a/index.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - Mantis React Admin Dashboard - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - \ No newline at end of file diff --git a/next/.gitignore b/next/.gitignore new file mode 100644 index 000000000..faa023a6d --- /dev/null +++ b/next/.gitignore @@ -0,0 +1,39 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.yarn* + +# local env files +.env*.local + + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +!package-lock.json diff --git a/next/.prettierignore b/next/.prettierignore new file mode 100644 index 000000000..db5ddae55 --- /dev/null +++ b/next/.prettierignore @@ -0,0 +1,2 @@ +.next +node_modules \ No newline at end of file diff --git a/.prettierrc b/next/.prettierrc similarity index 100% rename from .prettierrc rename to next/.prettierrc diff --git a/next/eslint.config.mjs b/next/eslint.config.mjs new file mode 100644 index 000000000..5730906ca --- /dev/null +++ b/next/eslint.config.mjs @@ -0,0 +1,89 @@ +import { fixupConfigRules } from '@eslint/compat'; +import prettier from 'eslint-plugin-prettier'; +import react from 'eslint-plugin-react'; +import reactHooks from 'eslint-plugin-react-hooks'; +import jsxA11y from 'eslint-plugin-jsx-a11y'; +import js from '@eslint/js'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { FlatCompat } from '@eslint/eslintrc'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const compat = new FlatCompat({ + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all +}); + +export default [ + ...fixupConfigRules(compat.extends('prettier')), + + { + plugins: { + prettier, + react, + 'react-hooks': reactHooks, + 'jsx-a11y': jsxA11y + }, + + languageOptions: { + ecmaVersion: 2020, + sourceType: 'module', + parserOptions: { + ecmaFeatures: { + jsx: true + } + } + }, + + settings: { + react: { + version: 'detect' + } + }, + + rules: { + 'react/jsx-filename-extension': 'off', + 'no-param-reassign': 'off', + 'react/prop-types': 'off', + 'react/require-default-props': 'off', + 'react/no-array-index-key': 'off', + 'react/react-in-jsx-scope': 'off', + 'react/jsx-props-no-spreading': 'off', + 'import/order': 'off', + 'no-console': 'off', + 'no-shadow': 'off', + 'import/no-cycle': 'off', + 'import/no-extraneous-dependencies': 'off', + 'jsx-a11y/label-has-associated-control': 'off', + 'jsx-a11y/no-autofocus': 'off', + 'react/jsx-uses-react': 'off', + 'react/jsx-uses-vars': 'error', + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + 'no-unused-vars': 'off', + + 'no-restricted-imports': [ + 'error', + { + patterns: ['@mui/*/*/*', '!@mui/material/test-utils/*'] + } + ], + + 'no-unused-vars': [ + 'error', + { + vars: 'all', + args: 'none' + } + ], + + 'prettier/prettier': 'warn' + } + }, + { + ignores: ['node_modules/**'], + files: ['src/**/*.{js,jsx}'] + } +]; \ No newline at end of file diff --git a/next/jsconfig.json b/next/jsconfig.json new file mode 100644 index 000000000..ffd0267e6 --- /dev/null +++ b/next/jsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "baseUrl": "src", + "downlevelIteration": true, + "allowSyntheticDefaultImports": true, + "noUnusedLocals": true, + "noImplicitAny": true, + "noFallthroughCasesInSwitch": true, + "noImplicitThis": true, + "strictNullChecks": true, + "typeRoots": ["../node_modules/@types", "../@types", "./types"], + "plugins": [ + { + "name": "next" + } + ] + }, + "include": ["next-env.d.js", "**/*.js", "**/*.jsx", ".next/types/**/*.js", ".next/dev/types/**/*.js"], + "exclude": ["node_modules"] +} diff --git a/next/next.config.js b/next/next.config.js new file mode 100644 index 000000000..2f79d6f0b --- /dev/null +++ b/next/next.config.js @@ -0,0 +1,22 @@ +/** @type {import('next').NextConfig} */ +const path = require('path'); + +const nextConfig = { + modularizeImports: { + '@mui/material': { + transform: '@mui/material/{{member}}' + } + }, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'flagcdn.com', + pathname: '**' + } + ] + }, + outputFileTracingRoot: path.join(__dirname, './') +}; + +module.exports = nextConfig; diff --git a/next/package.json b/next/package.json new file mode 100644 index 000000000..93444d9c1 --- /dev/null +++ b/next/package.json @@ -0,0 +1,64 @@ +{ + "name": "mantis-material-next-js", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"", + "lint:fix": "eslint --fix \"src/**/*.{js,jsx,ts,tsx}\"", + "prettier": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"" + }, + "dependencies": { + "@ant-design/colors": "8.0.1", + "@ant-design/icons": "6.2.3", + "@emotion/cache": "11.14.0", + "@emotion/react": "11.14.0", + "@emotion/styled": "11.14.1", + "@fontsource/inter": "5.2.8", + "@fontsource/poppins": "5.2.7", + "@fontsource/public-sans": "5.2.7", + "@fontsource/roboto": "5.2.10", + "@mui/lab": "9.0.0-beta.3", + "@mui/material": "7.3.9", + "@mui/material-nextjs": "9.0.1", + "@mui/system": "7.3.9", + "@mui/utils": "9.0.1", + "@mui/x-charts": "9.2.0", + "@mui/x-date-pickers": "9.2.0", + "apexcharts": "5.10.4", + "formik": "2.4.9", + "framer-motion": "12.36.0", + "lodash": "4.18.1", + "next": "16.1.6", + "prop-types": "15.8.1", + "react": "19.2.4", + "react-apexcharts": "2.1.0", + "react-copy-to-clipboard": "5.1.1", + "react-device-detect": "2.2.3", + "react-dom": "19.2.4", + "react-number-format": "5.4.5", + "simplebar-react": "3.3.2", + "styled-jsx": "5.1.7", + "swr": "2.4.1", + "yup": "1.7.1" + }, + "devDependencies": { + "@eslint/compat": "2.0.3", + "@eslint/eslintrc": "3.3.5", + "@eslint/js": "10.0.1", + "eslint": "10.1.0", + "eslint-config-next": "16.1.6", + "eslint-config-prettier": "10.1.8", + "eslint-plugin-jsx-a11y": "6.10.2", + "eslint-plugin-prettier": "5.5.5", + "eslint-plugin-react": "7.37.5", + "eslint-plugin-react-hooks": "7.0.1", + "prettier": "3.8.1" + }, + "overrides": { + "eslint": "10.1.0" + }, + "packageManager": "yarn@4.13.0" +} diff --git a/next/public/assets/fonts/inter/Inter-italic.var.woff2 b/next/public/assets/fonts/inter/Inter-italic.var.woff2 new file mode 100644 index 000000000..03875311a Binary files /dev/null and b/next/public/assets/fonts/inter/Inter-italic.var.woff2 differ diff --git a/next/public/assets/fonts/inter/Inter-roman.var.woff2 b/next/public/assets/fonts/inter/Inter-roman.var.woff2 new file mode 100644 index 000000000..a6efdc486 Binary files /dev/null and b/next/public/assets/fonts/inter/Inter-roman.var.woff2 differ diff --git a/next/public/assets/fonts/inter/inter.css b/next/public/assets/fonts/inter/inter.css new file mode 100644 index 000000000..0cdfce8f1 --- /dev/null +++ b/next/public/assets/fonts/inter/inter.css @@ -0,0 +1,17 @@ +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: 'Regular'; + src: url('Inter-roman.var.woff2?v=3.18') format('woff2'); +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: 'Italic'; + src: url('Inter-italic.var.woff2?v=3.18') format('woff2'); +} diff --git a/src/assets/images/users/avatar-1.png b/next/public/assets/images/users/avatar-1.png similarity index 100% rename from src/assets/images/users/avatar-1.png rename to next/public/assets/images/users/avatar-1.png diff --git a/src/assets/images/users/avatar-2.png b/next/public/assets/images/users/avatar-2.png similarity index 100% rename from src/assets/images/users/avatar-2.png rename to next/public/assets/images/users/avatar-2.png diff --git a/src/assets/images/users/avatar-3.png b/next/public/assets/images/users/avatar-3.png similarity index 100% rename from src/assets/images/users/avatar-3.png rename to next/public/assets/images/users/avatar-3.png diff --git a/src/assets/images/users/avatar-4.png b/next/public/assets/images/users/avatar-4.png similarity index 100% rename from src/assets/images/users/avatar-4.png rename to next/public/assets/images/users/avatar-4.png diff --git a/src/assets/images/users/avatar-5.png b/next/public/assets/images/users/avatar-5.png similarity index 100% rename from src/assets/images/users/avatar-5.png rename to next/public/assets/images/users/avatar-5.png diff --git a/next/public/assets/images/users/avatar-6.png b/next/public/assets/images/users/avatar-6.png new file mode 100644 index 000000000..c9e0bf62b Binary files /dev/null and b/next/public/assets/images/users/avatar-6.png differ diff --git a/src/assets/images/users/avatar-group.png b/next/public/assets/images/users/avatar-group.png similarity index 100% rename from src/assets/images/users/avatar-group.png rename to next/public/assets/images/users/avatar-group.png diff --git a/favicon.svg b/next/public/favicon.svg similarity index 100% rename from favicon.svg rename to next/public/favicon.svg diff --git a/src/assets/third-party/react-table.css b/next/public/third-party/react-table.css similarity index 100% rename from src/assets/third-party/react-table.css rename to next/public/third-party/react-table.css diff --git a/next/src/api/menu.js b/next/src/api/menu.js new file mode 100644 index 000000000..50b6b56d6 --- /dev/null +++ b/next/src/api/menu.js @@ -0,0 +1,45 @@ +'use client'; + +import useSWR, { mutate } from 'swr'; +import { useMemo } from 'react'; + +const initialState = { + isDashboardDrawerOpened: false +}; + +const endpoints = { + key: 'api/menu', + master: 'master', + dashboard: '/dashboard' // server URL +}; + +export function useGetMenuMaster() { + const { data, isLoading } = useSWR(endpoints.key + endpoints.master, () => initialState, { + fallbackData: initialState, + revalidateIfStale: false, + revalidateOnFocus: false, + revalidateOnReconnect: false + }); + + const memoizedValue = useMemo( + () => ({ + menuMaster: data || initialState, + menuMasterLoading: isLoading + }), + [data, isLoading] + ); + + return memoizedValue; +} + +export function handlerDrawerOpen(isDashboardDrawerOpened) { + // to update local state based on key + + mutate( + endpoints.key + endpoints.master, + (currentMenuMaster) => { + return { ...currentMenuMaster, isDashboardDrawerOpened }; + }, + false + ); +} diff --git a/next/src/app/(auth)/layout.jsx b/next/src/app/(auth)/layout.jsx new file mode 100644 index 000000000..4cad8cd42 --- /dev/null +++ b/next/src/app/(auth)/layout.jsx @@ -0,0 +1,9 @@ +import PropTypes from 'prop-types'; + +// ==============================|| AUTH LAYOUT ||============================== // + +export default function Layout({ children }) { + return <>{children}; +} + +Layout.propTypes = { children: PropTypes.node }; diff --git a/next/src/app/(auth)/login/page.jsx b/next/src/app/(auth)/login/page.jsx new file mode 100644 index 000000000..26e11e87a --- /dev/null +++ b/next/src/app/(auth)/login/page.jsx @@ -0,0 +1,8 @@ +// project imports +import SignIn from 'views/auth/login'; + +// ================================|| PAGE ||================================ // + +export default function SignInPage() { + return ; +} diff --git a/next/src/app/(auth)/register/page.jsx b/next/src/app/(auth)/register/page.jsx new file mode 100644 index 000000000..e38c979e0 --- /dev/null +++ b/next/src/app/(auth)/register/page.jsx @@ -0,0 +1,8 @@ +// project imports +import Register from 'views/auth/register'; + +// ================================|| REGISTER ||================================ // + +export default function RegisterPage() { + return ; +} diff --git a/next/src/app/(component)/color/page.jsx b/next/src/app/(component)/color/page.jsx new file mode 100644 index 000000000..d9c5de66c --- /dev/null +++ b/next/src/app/(component)/color/page.jsx @@ -0,0 +1,8 @@ +// project imports +import ComponentColor from 'views/components-overview/color'; + +// ===============================|| COMPONENTS - COLOR ||=============================== // + +export default function ColorPage() { + return ; +} diff --git a/next/src/app/(component)/layout.jsx b/next/src/app/(component)/layout.jsx new file mode 100644 index 000000000..5308806af --- /dev/null +++ b/next/src/app/(component)/layout.jsx @@ -0,0 +1,12 @@ +import PropTypes from 'prop-types'; + +// project imports +import DashboardLayout from 'layout/Dashboard'; + +// ==============================|| COMPONENT - LAYOUT ||============================== // + +export default function Layout({ children }) { + return {children}; +} + +Layout.propTypes = { children: PropTypes.node }; diff --git a/next/src/app/(component)/shadows/page.jsx b/next/src/app/(component)/shadows/page.jsx new file mode 100644 index 000000000..c001a3ffe --- /dev/null +++ b/next/src/app/(component)/shadows/page.jsx @@ -0,0 +1,8 @@ +// project imports +import ComponentShadow from 'views/components-overview/shadows'; + +// ============================|| COMPONENTS - SHADOW ||============================ // + +export default function ShadowPage() { + return ; +} diff --git a/next/src/app/(component)/typography/page.jsx b/next/src/app/(component)/typography/page.jsx new file mode 100644 index 000000000..cbe75b6d0 --- /dev/null +++ b/next/src/app/(component)/typography/page.jsx @@ -0,0 +1,8 @@ +// project imports +import ComponentTypography from 'views/components-overview/typography'; + +// ==============================|| COMPONENTS - TYPOGRAPHY ||============================== // + +export default function TypographyPage() { + return ; +} diff --git a/next/src/app/(dashboard)/dashboard/default/page.jsx b/next/src/app/(dashboard)/dashboard/default/page.jsx new file mode 100644 index 000000000..14414c9a3 --- /dev/null +++ b/next/src/app/(dashboard)/dashboard/default/page.jsx @@ -0,0 +1,8 @@ +// project imports +import DashboardDefault from 'views/dashboard/default'; + +// ==============================|| DASHBOARD - DEFAULT ||============================== // + +export default function Dashboard() { + return ; +} diff --git a/next/src/app/(dashboard)/dashboard/loading.jsx b/next/src/app/(dashboard)/dashboard/loading.jsx new file mode 100644 index 000000000..b85c71f17 --- /dev/null +++ b/next/src/app/(dashboard)/dashboard/loading.jsx @@ -0,0 +1,7 @@ +import Loader from 'components/Loader'; + +// ==============================|| DASHBOARD - LOADING ||============================== // + +export default function Loading() { + return ; +} diff --git a/next/src/app/(dashboard)/dashboard/page.jsx b/next/src/app/(dashboard)/dashboard/page.jsx new file mode 100644 index 000000000..f8ce0e808 --- /dev/null +++ b/next/src/app/(dashboard)/dashboard/page.jsx @@ -0,0 +1,5 @@ +import { redirect } from 'next/navigation'; + +export default function DashboardPage() { + redirect('/dashboard/default'); +} diff --git a/next/src/app/(dashboard)/layout.jsx b/next/src/app/(dashboard)/layout.jsx new file mode 100644 index 000000000..35327fb30 --- /dev/null +++ b/next/src/app/(dashboard)/layout.jsx @@ -0,0 +1,14 @@ +'use client'; + +import PropTypes from 'prop-types'; + +// project imports +import DashboardLayout from 'layout/Dashboard'; + +// ==============================|| DASHBOARD LAYOUT ||============================== // + +export default function Layout({ children }) { + return {children}; +} + +Layout.propTypes = { children: PropTypes.node }; diff --git a/next/src/app/(dashboard)/loading.jsx b/next/src/app/(dashboard)/loading.jsx new file mode 100644 index 000000000..b85c71f17 --- /dev/null +++ b/next/src/app/(dashboard)/loading.jsx @@ -0,0 +1,7 @@ +import Loader from 'components/Loader'; + +// ==============================|| DASHBOARD - LOADING ||============================== // + +export default function Loading() { + return ; +} diff --git a/next/src/app/(dashboard)/sample-page/loading.jsx b/next/src/app/(dashboard)/sample-page/loading.jsx new file mode 100644 index 000000000..b32ed4380 --- /dev/null +++ b/next/src/app/(dashboard)/sample-page/loading.jsx @@ -0,0 +1,7 @@ +import Loader from 'components/Loader'; + +// ==============================|| SAMPLE PAGE - LOADING ||============================== // + +export default function Loading() { + return ; +} diff --git a/next/src/app/(dashboard)/sample-page/page.jsx b/next/src/app/(dashboard)/sample-page/page.jsx new file mode 100644 index 000000000..d92768b4c --- /dev/null +++ b/next/src/app/(dashboard)/sample-page/page.jsx @@ -0,0 +1,8 @@ +// project imports +import SamplePagePage from 'views/other/sample-page'; + +// ==============================|| SAMPLE PAGE ||============================== // + +export default function SamplePage() { + return ; +} diff --git a/next/src/app/(simple)/layout.jsx b/next/src/app/(simple)/layout.jsx new file mode 100644 index 000000000..26dcdc11c --- /dev/null +++ b/next/src/app/(simple)/layout.jsx @@ -0,0 +1,12 @@ +import PropTypes from 'prop-types'; + +// project imports +import SimpleLayout from 'layout/Simple'; + +// ================================|| SIMPLE LAYOUT ||================================ // + +export default function Layout({ children }) { + return {children}; +} + +Layout.propTypes = { children: PropTypes.node }; diff --git a/next/src/app/ProviderWrapper.jsx b/next/src/app/ProviderWrapper.jsx new file mode 100644 index 000000000..445fc2e0d --- /dev/null +++ b/next/src/app/ProviderWrapper.jsx @@ -0,0 +1,20 @@ +import PropTypes from 'prop-types'; + +// project imports +import { ConfigProvider } from 'contexts/ConfigContext'; +import ScrollTop from 'components/ScrollTop'; +import ThemeCustomization from '../../themes'; + +// ==============================|| PROVIDER WRAPPER ||============================== // + +export default function ProviderWrapper({ children }) { + return ( + + + {children} + + + ); +} + +ProviderWrapper.propTypes = { children: PropTypes.node }; diff --git a/next/src/app/favicon.ico b/next/src/app/favicon.ico new file mode 100644 index 000000000..16ee9f573 Binary files /dev/null and b/next/src/app/favicon.ico differ diff --git a/next/src/app/layout.jsx b/next/src/app/layout.jsx new file mode 100644 index 000000000..9b6515875 --- /dev/null +++ b/next/src/app/layout.jsx @@ -0,0 +1,37 @@ +import PropTypes from 'prop-types'; + +// third-party +import 'simplebar-react/dist/simplebar.min.css'; + +// fonts +import '@fontsource/public-sans/400.css'; +import '@fontsource/public-sans/500.css'; +import '@fontsource/public-sans/600.css'; +import '@fontsource/public-sans/700.css'; + +// project-imports +import ProviderWrapper from './ProviderWrapper'; + +export const metadata = { + title: 'Mantis Next.js Admin Dashboard Template', + description: + 'Start your next Next.js project with the Mantis admin template. It is built with ReactJS, Material-UI, NextJS, and SWR for faster web development.', + keywords: + 'nextjs admin template, material-ui react dashboard template, reactjs admin template, reactjs dashboard, react backend template', + author: 'CodedThemes' +}; + +export default function RootLayout({ children }) { + return ( + + + + + + {children} + + + ); +} + +RootLayout.propTypes = { children: PropTypes.node }; diff --git a/next/src/app/loading.jsx b/next/src/app/loading.jsx new file mode 100644 index 000000000..3acb4f41a --- /dev/null +++ b/next/src/app/loading.jsx @@ -0,0 +1,8 @@ +// project imports +import Loader from 'components/Loader'; + +// ==============================|| LOADING ||============================== // + +export default function Loading() { + return ; +} diff --git a/next/src/app/page.jsx b/next/src/app/page.jsx new file mode 100644 index 000000000..5e399a771 --- /dev/null +++ b/next/src/app/page.jsx @@ -0,0 +1,7 @@ +import { redirect } from 'next/navigation'; + +// ==============================|| DASHBOARD ||============================== // + +export default function Landing() { + redirect('/dashboard/default'); +} diff --git a/next/src/components/@extended/AnimateButton.jsx b/next/src/components/@extended/AnimateButton.jsx new file mode 100644 index 000000000..a76288dde --- /dev/null +++ b/next/src/components/@extended/AnimateButton.jsx @@ -0,0 +1,78 @@ +'use client'; +import PropTypes from 'prop-types'; + +// third-party +import { motion, useCycle } from 'framer-motion'; + +export default function AnimateButton({ children, type = 'scale', direction = 'right', offset = 10, scale = { hover: 1.05, tap: 0.954 } }) { + let offset1; + let offset2; + switch (direction) { + case 'up': + case 'left': + offset1 = offset; + offset2 = 0; + break; + case 'right': + case 'down': + default: + offset1 = 0; + offset2 = offset; + break; + } + + const [x, cycleX] = useCycle(offset1, offset2); + const [y, cycleY] = useCycle(offset1, offset2); + + switch (type) { + case 'rotate': + return ( + + {children} + + ); + case 'slide': + if (direction === 'up' || direction === 'down') { + return ( + cycleY()} onHoverStart={() => cycleY()}> + {children} + + ); + } + return ( + cycleX()} onHoverStart={() => cycleX()}> + {children} + + ); + + case 'scale': + default: + if (typeof scale === 'number') { + scale = { + hover: scale, + tap: scale + }; + } + return ( + + {children} + + ); + } +} + +AnimateButton.propTypes = { + children: PropTypes.node, + type: PropTypes.oneOf(['slide', 'scale', 'rotate']), + direction: PropTypes.oneOf(['up', 'down', 'left', 'right']), + offset: PropTypes.number, + scale: PropTypes.object +}; diff --git a/src/components/@extended/Avatar.jsx b/next/src/components/@extended/Avatar.jsx similarity index 97% rename from src/components/@extended/Avatar.jsx rename to next/src/components/@extended/Avatar.jsx index bcc9549be..b43585126 100644 --- a/src/components/@extended/Avatar.jsx +++ b/next/src/components/@extended/Avatar.jsx @@ -110,7 +110,7 @@ export default function Avatar({ children, color = 'primary', type, size = 'md', getColorStyle.propTypes = { theme: PropTypes.any, color: PropTypes.any, type: PropTypes.any }; Avatar.propTypes = { - children: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + children: PropTypes.oneOfType([PropTypes.node, PropTypes.string]), color: PropTypes.string, type: PropTypes.any, size: PropTypes.string, diff --git a/next/src/components/@extended/Breadcrumbs.jsx b/next/src/components/@extended/Breadcrumbs.jsx new file mode 100644 index 000000000..583afc72a --- /dev/null +++ b/next/src/components/@extended/Breadcrumbs.jsx @@ -0,0 +1,246 @@ +'use client'; + +import PropTypes from 'prop-types'; +import { useEffect, useState } from 'react'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; + +// material-ui +import { useTheme } from '@mui/material/styles'; +import Divider from '@mui/material/Divider'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; +import MuiBreadcrumbs from '@mui/material/Breadcrumbs'; + +// project imports +import MainCard from 'components/MainCard'; +import navigation from 'menu-items'; + +// assets +import ApartmentOutlined from '@ant-design/icons/ApartmentOutlined'; +import HomeOutlined from '@ant-design/icons/HomeOutlined'; +import HomeFilled from '@ant-design/icons/HomeFilled'; + +export default function Breadcrumbs({ + card = false, + custom = false, + divider = false, + heading, + icon, + icons, + links, + maxItems, + rightAlign, + separator, + title = true, + titleBottom = true, + sx, + ...others +}) { + const theme = useTheme(); + const pathname = usePathname(); + + const [main, setMain] = useState(); + const [item, setItem] = useState(); + + const iconSX = { + marginRight: theme.spacing(0.75), + marginLeft: 0, + width: '1rem', + height: '1rem', + color: theme.vars.palette.secondary.main + }; + + let customLocation = pathname; + + // only used for component demo breadcrumbs + if (customLocation.includes('/components-overview/breadcrumbs')) { + customLocation = '/apps/customer/customer-card'; + } + + useEffect(() => { + navigation?.items?.map((menu) => { + if (menu.type && menu.type === 'group') { + if (menu?.url && menu.url === customLocation) { + setMain(menu); + setItem(menu); + } else { + getCollapse(menu); + } + } + return false; + }); + }); + + // set active item state + const getCollapse = (menu) => { + if (!custom && menu.children) { + menu.children.filter((collapse) => { + if (collapse.type && collapse.type === 'collapse') { + getCollapse(collapse); + if (collapse.url === customLocation) { + setMain(collapse); + setItem(collapse); + } + } else if (collapse.type && collapse.type === 'item') { + if (customLocation === collapse.url) { + setMain(menu); + setItem(collapse); + } + } + return false; + }); + } + }; + + // item separator + const SeparatorIcon = separator; + const separatorIcon = separator ? : '/'; + + let mainContent; + let itemContent; + let breadcrumbContent = ; + let itemTitle = ''; + let CollapseIcon; + let ItemIcon; + + // collapse item + if (main && main.type === 'collapse' && !main.breadcrumbs) { + CollapseIcon = main.icon ? main.icon : ApartmentOutlined; + mainContent = ( + + {icons && } + {main?.title} + + ); + + if (!!custom) { + breadcrumbContent = ( + + + + + {icons && } + {icon && !icons && } + {(!icon || icons) && 'Home'} + + {mainContent} + + {title && titleBottom && ( + + {main.title} + + )} + + {card === false && divider !== false && } + + ); + } + } + + // items + if ((item && item.type === 'item') || (item?.type === 'group' && item?.url) || custom) { + itemTitle = item?.title; + + ItemIcon = item?.icon ? item.icon : ApartmentOutlined; + itemContent = ( + + {icons && } + {itemTitle} + + ); + + let tempContent = ( + + + {icons && } + {icon && !icons && } + {(!icon || icons) && 'Home'} + + {mainContent} + {itemContent} + + ); + + if (custom && links && links?.length > 0) { + tempContent = ( + + {links?.map((link, index) => { + CollapseIcon = link.icon ? link.icon : ApartmentOutlined; + + return ( + + {link.icon && } + {link.title} + + ); + })} + + ); + } + + // main + if (item?.breadcrumbs !== false || custom) { + breadcrumbContent = ( + + + {title && !titleBottom && {custom ? heading : item?.title}} + {tempContent} + {title && titleBottom && ( + + {custom ? heading : item?.title} + + )} + + {card === false && divider !== false && } + + ); + } + } + + return breadcrumbContent; +} + +Breadcrumbs.propTypes = { + card: PropTypes.bool, + custom: PropTypes.bool, + divider: PropTypes.bool, + heading: PropTypes.string, + icon: PropTypes.bool, + icons: PropTypes.bool, + links: PropTypes.array, + maxItems: PropTypes.number, + rightAlign: PropTypes.bool, + separator: PropTypes.any, + title: PropTypes.bool, + titleBottom: PropTypes.bool, + sx: PropTypes.any, + others: PropTypes.any +}; diff --git a/next/src/components/@extended/Dot.jsx b/next/src/components/@extended/Dot.jsx new file mode 100644 index 000000000..7bb2144b1 --- /dev/null +++ b/next/src/components/@extended/Dot.jsx @@ -0,0 +1,31 @@ +'use client'; + +import PropTypes from 'prop-types'; + +// material-ui +import { useTheme } from '@mui/material/styles'; +import Box from '@mui/material/Box'; + +// project imports +import getColors from 'utils/getColors'; + +export default function Dot({ color, size, variant, sx, ...rest }) { + const theme = useTheme(); + const colors = getColors(theme, color || 'primary'); + const { main } = colors; + + return ( + + ); +} + +Dot.propTypes = { color: PropTypes.any, size: PropTypes.number, variant: PropTypes.string, sx: PropTypes.any, rest: PropTypes.any }; diff --git a/src/components/@extended/IconButton.jsx b/next/src/components/@extended/IconButton.jsx similarity index 100% rename from src/components/@extended/IconButton.jsx rename to next/src/components/@extended/IconButton.jsx diff --git a/src/components/@extended/Transitions.jsx b/next/src/components/@extended/Transitions.jsx similarity index 100% rename from src/components/@extended/Transitions.jsx rename to next/src/components/@extended/Transitions.jsx index a2ec1c369..b1a3b4234 100644 --- a/src/components/@extended/Transitions.jsx +++ b/next/src/components/@extended/Transitions.jsx @@ -112,8 +112,8 @@ Transitions.propTypes = { children: PropTypes.node, position: PropTypes.string, type: PropTypes.string, - ref: PropTypes.any, direction: PropTypes.oneOf(['up', 'right', 'left', 'down']), + ref: PropTypes.any, others: PropTypes.any }; diff --git a/src/components/ContainerWrapper.jsx b/next/src/components/ContainerWrapper.jsx similarity index 97% rename from src/components/ContainerWrapper.jsx rename to next/src/components/ContainerWrapper.jsx index 0e1b9cb32..b84b1372b 100644 --- a/src/components/ContainerWrapper.jsx +++ b/next/src/components/ContainerWrapper.jsx @@ -1,4 +1,7 @@ +'use client'; + import PropTypes from 'prop-types'; + // project imports import useMediaQuery from '@mui/material/useMediaQuery'; import Container from '@mui/material/Container'; diff --git a/src/components/Loader.jsx b/next/src/components/Loader.jsx similarity index 100% rename from src/components/Loader.jsx rename to next/src/components/Loader.jsx diff --git a/src/components/MainCard.jsx b/next/src/components/MainCard.jsx similarity index 89% rename from src/components/MainCard.jsx rename to next/src/components/MainCard.jsx index 14b7933a8..f8471348e 100644 --- a/src/components/MainCard.jsx +++ b/next/src/components/MainCard.jsx @@ -13,7 +13,6 @@ export default function MainCard({ subheader, content = true, contentSX = {}, - darkTitle, divider = true, elevation, secondary, @@ -52,11 +51,11 @@ export default function MainCard({ {...others} > {/* card header and action */} - {!darkTitle && title && ( + {title && ( - + Β© Made with love by Team{' '} CodedThemes @@ -25,21 +25,21 @@ export default function AuthFooter() { Terms and Conditions Privacy Policy diff --git a/src/components/cards/statistics/AnalyticEcommerce.jsx b/next/src/components/cards/statistics/AnalyticEcommerce.jsx similarity index 63% rename from src/components/cards/statistics/AnalyticEcommerce.jsx rename to next/src/components/cards/statistics/AnalyticEcommerce.jsx index 8851d6913..b4ce318b1 100644 --- a/src/components/cards/statistics/AnalyticEcommerce.jsx +++ b/next/src/components/cards/statistics/AnalyticEcommerce.jsx @@ -1,7 +1,6 @@ import PropTypes from 'prop-types'; // material-ui import Chip from '@mui/material/Chip'; -import Grid from '@mui/material/Grid'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; @@ -19,31 +18,27 @@ export default function AnalyticEcommerce({ color = 'primary', title, count, per return ( - + {title} - - - - {count} - - + + + {count} + {percentage && ( - - : } - label={`${percentage}%`} - sx={{ ml: 1.25, pl: 1 }} - size="small" - /> - + : } + label={`${percentage}%`} + sx={{ ml: 1.25, pl: 1 }} + size="small" + /> )} - + - + You made an extra{' '} {extra} diff --git a/src/components/logo/LogoIcon.jsx b/next/src/components/logo/LogoIcon.jsx similarity index 100% rename from src/components/logo/LogoIcon.jsx rename to next/src/components/logo/LogoIcon.jsx diff --git a/next/src/components/logo/LogoMain.jsx b/next/src/components/logo/LogoMain.jsx new file mode 100644 index 000000000..f81773f56 --- /dev/null +++ b/next/src/components/logo/LogoMain.jsx @@ -0,0 +1,47 @@ +'use client'; + +// material-ui +import { useTheme } from '@mui/material/styles'; + +// ==============================|| LOGO SVG ||============================== // + +export default function LogoMain() { + const theme = useTheme(); + return ( + <> + + + + + + + + + + + + + + + + + + + ); +} diff --git a/next/src/components/logo/index.jsx b/next/src/components/logo/index.jsx new file mode 100644 index 000000000..666ec8235 --- /dev/null +++ b/next/src/components/logo/index.jsx @@ -0,0 +1,19 @@ +import PropTypes from 'prop-types'; +import ButtonBase from '@mui/material/ButtonBase'; + +// project imports +import Logo from './LogoMain'; +import LogoIcon from './LogoIcon'; +import { NextLink } from 'components/routes'; + +import { APP_DEFAULT_PATH } from 'config'; + +export default function LogoSection({ reverse, isIcon, sx, to }) { + return ( + + {isIcon ? : } + + ); +} + +LogoSection.propTypes = { reverse: PropTypes.bool, isIcon: PropTypes.bool, sx: PropTypes.any, to: PropTypes.string }; diff --git a/next/src/components/routes/index.js b/next/src/components/routes/index.js new file mode 100644 index 000000000..587f0afe9 --- /dev/null +++ b/next/src/components/routes/index.js @@ -0,0 +1 @@ +export * from './router-link'; diff --git a/next/src/components/routes/router-link.jsx b/next/src/components/routes/router-link.jsx new file mode 100644 index 000000000..25516436d --- /dev/null +++ b/next/src/components/routes/router-link.jsx @@ -0,0 +1,23 @@ +'use client'; + +/** + * Client-only wrapper for Next.js `Link`. + * + * Why this file exists: + * - Next.js 16 enforces a strict separation between Server and Client Components. + * - MUI components (e.g. Button, Link, MenuItem) require `NextLink` to be + * imported from a Client Component. + * - Importing `next/link` directly inside Server Components causes build + * or prerender errors. + * + * Usage: + * ```ts + * import { NextLink } from 'components/NextLink'; + * ``` + * + * Reference: + * https://mui.com/material-ui/integrations/nextjs/#next-js-v16-client-component-restriction + */ +import NextLink from 'next/link'; + +export { NextLink }; diff --git a/src/components/third-party/SimpleBar.jsx b/next/src/components/third-party/SimpleBar.jsx similarity index 100% rename from src/components/third-party/SimpleBar.jsx rename to next/src/components/third-party/SimpleBar.jsx diff --git a/next/src/components/third-party/index.js b/next/src/components/third-party/index.js new file mode 100644 index 000000000..f8375bc21 --- /dev/null +++ b/next/src/components/third-party/index.js @@ -0,0 +1,3 @@ +'use client'; + +export { NumericFormat } from 'react-number-format'; diff --git a/src/config.js b/next/src/config.js similarity index 100% rename from src/config.js rename to next/src/config.js diff --git a/next/src/contexts/ConfigContext.jsx b/next/src/contexts/ConfigContext.jsx new file mode 100644 index 000000000..88f69a855 --- /dev/null +++ b/next/src/contexts/ConfigContext.jsx @@ -0,0 +1,24 @@ +'use client'; + +import PropTypes from 'prop-types'; +import { createContext, useMemo } from 'react'; + +// project imports +import config from 'config'; +import { useLocalStorage } from 'hooks/useLocalStorage'; + +// ==============================|| CONFIG CONTEXT ||============================== // + +export const ConfigContext = createContext(undefined); + +// ==============================|| CONFIG PROVIDER ||============================== // + +export function ConfigProvider({ children }) { + const { state, setState, setField, resetState } = useLocalStorage('mantis-react-js-config', config); + + const memoizedValue = useMemo(() => ({ state, setState, setField, resetState }), [state, setField, setState, resetState]); + + return {children}; +} + +ConfigProvider.propTypes = { children: PropTypes.node }; diff --git a/src/contexts/README.md b/next/src/contexts/README.md similarity index 100% rename from src/contexts/README.md rename to next/src/contexts/README.md diff --git a/src/data/README.md b/next/src/data/README.md similarity index 100% rename from src/data/README.md rename to next/src/data/README.md diff --git a/src/hooks/README.md b/next/src/hooks/README.md similarity index 100% rename from src/hooks/README.md rename to next/src/hooks/README.md diff --git a/src/hooks/useConfig.js b/next/src/hooks/useConfig.js similarity index 100% rename from src/hooks/useConfig.js rename to next/src/hooks/useConfig.js diff --git a/next/src/hooks/useLocalStorage.js b/next/src/hooks/useLocalStorage.js new file mode 100644 index 000000000..5f511d533 --- /dev/null +++ b/next/src/hooks/useLocalStorage.js @@ -0,0 +1,46 @@ +'use client'; + +import { useState, useEffect, useCallback } from 'react'; + +// ==============================|| LOCAL STORAGE HOOKS ||============================== // + +export function useLocalStorage(key, defaultValue) { + const [state, setState] = useState(defaultValue); + + // Load initial state from localStorage after mount to be hydration-safe + useEffect(() => { + try { + const item = localStorage.getItem(key); + if (item) { + setState(JSON.parse(item)); + } + } catch (err) { + console.warn(`Error reading localStorage key β€œ${key}”:`, err); + } + }, [key]); + + // Sync to localStorage whenever state changes + useEffect(() => { + try { + localStorage.setItem(key, JSON.stringify(state)); + } catch (err) { + console.warn(`Error setting localStorage key β€œ${key}”:`, err); + } + }, [key, state]); + + // Update single field + const setField = useCallback((key, value) => { + setState((prev) => ({ + ...prev, + [key]: value + })); + }, []); + + // Reset to defaults + const resetState = useCallback(() => { + setState(defaultValue); + localStorage.setItem(key, JSON.stringify(defaultValue)); + }, [defaultValue, key]); + + return { state, setState, setField, resetState }; +} diff --git a/next/src/layout/Dashboard/Drawer/DrawerContent/NavCard.jsx b/next/src/layout/Dashboard/Drawer/DrawerContent/NavCard.jsx new file mode 100644 index 000000000..0449f7986 --- /dev/null +++ b/next/src/layout/Dashboard/Drawer/DrawerContent/NavCard.jsx @@ -0,0 +1,35 @@ +// material-ui +import Button from '@mui/material/Button'; +import CardMedia from '@mui/material/CardMedia'; +import Link from '@mui/material/Link'; +import Stack from '@mui/material/Stack'; +import Typography from '@mui/material/Typography'; + +// project import +import AnimateButton from 'components/@extended/AnimateButton'; +import MainCard from 'components/MainCard'; + +const avatar = '/assets/images/users/avatar-group.png'; + +// ==============================|| DRAWER CONTENT - NAVIGATION CARD ||============================== // + +export default function NavCard() { + return ( + + + + + Mantis Pro + + Checkout pro features + + + + + + + + ); +} diff --git a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavGroup.jsx b/next/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavGroup.jsx similarity index 85% rename from src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavGroup.jsx rename to next/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavGroup.jsx index 97476698b..69d49410c 100644 --- a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavGroup.jsx +++ b/next/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavGroup.jsx @@ -1,4 +1,7 @@ +'use client'; + import PropTypes from 'prop-types'; + // material-ui import List from '@mui/material/List'; import Typography from '@mui/material/Typography'; @@ -18,7 +21,7 @@ export default function NavGroup({ item }) { switch (menuItem.type) { case 'collapse': return ( - + collapse - only available in paid version ); @@ -26,7 +29,7 @@ export default function NavGroup({ item }) { return ; default: return ( - + Fix - Group Collapse or Items ); @@ -39,7 +42,7 @@ export default function NavGroup({ item }) { item.title && drawerOpen && ( - + {item.title} {/* only available in paid version */} diff --git a/next/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavItem.jsx b/next/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavItem.jsx new file mode 100644 index 000000000..f4b871102 --- /dev/null +++ b/next/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavItem.jsx @@ -0,0 +1,180 @@ +'use client'; + +import PropTypes from 'prop-types'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; + +// material-ui +import useMediaQuery from '@mui/material/useMediaQuery'; +import Avatar from '@mui/material/Avatar'; +import Chip from '@mui/material/Chip'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; + +// project imports +import IconButton from 'components/@extended/IconButton'; + +import { handlerDrawerOpen, useGetMenuMaster } from 'api/menu'; + +// ==============================|| NAVIGATION - LIST ITEM ||============================== // + +export default function NavItem({ item, level, isParents = false, setSelectedID }) { + const { menuMaster } = useGetMenuMaster(); + const drawerOpen = menuMaster.isDashboardDrawerOpened; + + const downLG = useMediaQuery((theme) => theme.breakpoints.down('lg')); + + let itemTarget = '_self'; + if (item.target) { + itemTarget = '_blank'; + } + + const itemHandler = () => { + if (downLG) handlerDrawerOpen(false); + + if (isParents && setSelectedID) { + setSelectedID(item.id); + } + }; + + const Icon = item.icon; + const itemIcon = item.icon ? ( + + ) : ( + false + ); + + const pathname = usePathname(); + const isSelected = item.url === pathname || (item.url !== '/' && pathname.startsWith(item.url)); + + const textColor = 'text.primary'; + const iconSelectedColor = 'primary.main'; + + return ( + <> + + ({ + zIndex: 1201, + pl: drawerOpen ? `${level * 28}px` : 1.5, + py: !drawerOpen && level === 1 ? 1.25 : 1, + ...(drawerOpen && { + '&:hover': { bgcolor: 'primary.lighter' }, + '&.Mui-selected': { + bgcolor: 'primary.lighter', + borderRight: '2px solid', + borderColor: 'primary.main', + color: iconSelectedColor, + '&:hover': { color: iconSelectedColor, bgcolor: 'primary.lighter' } + } + }), + ...(!drawerOpen && { + '&:hover': { bgcolor: 'transparent' }, + '&.Mui-selected': { '&:hover': { bgcolor: 'transparent' }, bgcolor: 'transparent' } + }) + })} + onClick={() => itemHandler()} + > + {itemIcon && ( + ({ + minWidth: 28, + color: isSelected ? iconSelectedColor : textColor, + ...(!drawerOpen && { + borderRadius: 1.5, + width: 36, + height: 36, + alignItems: 'center', + justifyContent: 'center', + '&:hover': { bgcolor: 'secondary.lighter' } + }), + ...(!drawerOpen && + isSelected && { + bgcolor: 'primary.lighter', + '&:hover': { bgcolor: 'primary.lighter' } + }) + })} + > + {itemIcon} + + )} + {(drawerOpen || (!drawerOpen && level !== 1)) && ( + + {item.title} + + } + /> + )} + {(drawerOpen || (!drawerOpen && level !== 1)) && item.chip && ( + {item.chip.avatar}} + /> + )} + + {(drawerOpen || (!drawerOpen && level !== 1)) && + item?.actions && + item?.actions.map((action, index) => { + const ActionIcon = action.icon; + const callAction = action?.function; + return ( + { + event.stopPropagation(); + callAction(); + } + })} + component={Link} + href={action.url} + target={action.target ? '_blank' : '_self'} + color="secondary" + variant="outlined" + sx={{ + position: 'absolute', + top: 12, + right: 20, + zIndex: 1202, + width: 20, + height: 20, + mr: -1, + ml: 1, + color: 'secondary.dark', + borderColor: isSelected ? 'primary.light' : 'secondary.light', + '&:hover': { borderColor: isSelected ? 'primary.main' : 'secondary.main' } + }} + > + + + ); + })} + + + ); +} + +NavItem.propTypes = { + item: PropTypes.any, + level: PropTypes.number, + isParents: PropTypes.bool, + setSelectedID: PropTypes.oneOfType([PropTypes.any, PropTypes.func]) +}; diff --git a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/index.jsx b/next/src/layout/Dashboard/Drawer/DrawerContent/Navigation/index.jsx similarity index 86% rename from src/layout/Dashboard/Drawer/DrawerContent/Navigation/index.jsx rename to next/src/layout/Dashboard/Drawer/DrawerContent/Navigation/index.jsx index 765054812..664114303 100644 --- a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/index.jsx +++ b/next/src/layout/Dashboard/Drawer/DrawerContent/Navigation/index.jsx @@ -15,7 +15,7 @@ export default function Navigation() { return ; default: return ( - + Fix - Navigation Group ); diff --git a/next/src/layout/Dashboard/Drawer/DrawerContent/index.jsx b/next/src/layout/Dashboard/Drawer/DrawerContent/index.jsx new file mode 100644 index 000000000..4249c6aa6 --- /dev/null +++ b/next/src/layout/Dashboard/Drawer/DrawerContent/index.jsx @@ -0,0 +1,23 @@ +'use client'; + +// project imports +import NavCard from './NavCard'; +import Navigation from './Navigation'; +import SimpleBar from 'components/third-party/SimpleBar'; +import { useGetMenuMaster } from 'api/menu'; + +// ==============================|| DRAWER CONTENT ||============================== // + +export default function DrawerContent() { + const { menuMaster } = useGetMenuMaster(); + const drawerOpen = menuMaster.isDashboardDrawerOpened; + + return ( + <> + + + {drawerOpen && } + + + ); +} diff --git a/src/layout/Dashboard/Drawer/DrawerHeader/DrawerHeaderStyled.js b/next/src/layout/Dashboard/Drawer/DrawerHeader/DrawerHeaderStyled.js similarity index 100% rename from src/layout/Dashboard/Drawer/DrawerHeader/DrawerHeaderStyled.js rename to next/src/layout/Dashboard/Drawer/DrawerHeader/DrawerHeaderStyled.js diff --git a/src/layout/Dashboard/Drawer/DrawerHeader/index.jsx b/next/src/layout/Dashboard/Drawer/DrawerHeader/index.jsx similarity index 100% rename from src/layout/Dashboard/Drawer/DrawerHeader/index.jsx rename to next/src/layout/Dashboard/Drawer/DrawerHeader/index.jsx diff --git a/src/layout/Dashboard/Drawer/MiniDrawerStyled.js b/next/src/layout/Dashboard/Drawer/MiniDrawerStyled.js similarity index 100% rename from src/layout/Dashboard/Drawer/MiniDrawerStyled.js rename to next/src/layout/Dashboard/Drawer/MiniDrawerStyled.js diff --git a/next/src/layout/Dashboard/Drawer/index.jsx b/next/src/layout/Dashboard/Drawer/index.jsx new file mode 100644 index 000000000..eb2bae23b --- /dev/null +++ b/next/src/layout/Dashboard/Drawer/index.jsx @@ -0,0 +1,68 @@ +'use client'; + +import PropTypes from 'prop-types'; +import { useMemo } from 'react'; + +// material-ui +import Drawer from '@mui/material/Drawer'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import Box from '@mui/material/Box'; + +// project imports +import DrawerHeader from './DrawerHeader'; +import DrawerContent from './DrawerContent'; +import MiniDrawerStyled from './MiniDrawerStyled'; + +import { DRAWER_WIDTH } from 'config'; +import { handlerDrawerOpen, useGetMenuMaster } from 'api/menu'; + +// ==============================|| MAIN LAYOUT - DRAWER ||============================== // + +export default function MainDrawer({ window }) { + const { menuMaster } = useGetMenuMaster(); + const drawerOpen = menuMaster.isDashboardDrawerOpened; + const downLG = useMediaQuery((theme) => theme.breakpoints.down('lg')); + + // responsive drawer container + const container = window !== undefined ? () => window().document.body : undefined; + + // header content + const drawerContent = useMemo(() => , []); + const drawerHeader = useMemo(() => , [drawerOpen]); + + return ( + + {!downLG ? ( + + {drawerHeader} + {drawerContent} + + ) : ( + handlerDrawerOpen(!drawerOpen)} + ModalProps={{ keepMounted: true }} + sx={{ display: { xs: drawerOpen ? 'block' : 'none', lg: 'none' } }} + slotProps={{ + paper: { + sx: { + boxSizing: 'border-box', + width: DRAWER_WIDTH, + borderRight: '1px solid', + borderRightColor: 'divider', + boxShadow: 'inherit' + } + } + }} + > + {drawerHeader} + {drawerContent} + + )} + + ); +} + +MainDrawer.propTypes = { window: PropTypes.func }; diff --git a/src/layout/Dashboard/Footer.jsx b/next/src/layout/Dashboard/Footer.jsx similarity index 100% rename from src/layout/Dashboard/Footer.jsx rename to next/src/layout/Dashboard/Footer.jsx diff --git a/src/layout/Dashboard/Header/AppBarStyled.jsx b/next/src/layout/Dashboard/Header/AppBarStyled.jsx similarity index 100% rename from src/layout/Dashboard/Header/AppBarStyled.jsx rename to next/src/layout/Dashboard/Header/AppBarStyled.jsx diff --git a/src/layout/Dashboard/Header/HeaderContent/MobileSection.jsx b/next/src/layout/Dashboard/Header/HeaderContent/MobileSection.jsx similarity index 97% rename from src/layout/Dashboard/Header/HeaderContent/MobileSection.jsx rename to next/src/layout/Dashboard/Header/HeaderContent/MobileSection.jsx index ae6c1086c..1346d10df 100644 --- a/src/layout/Dashboard/Header/HeaderContent/MobileSection.jsx +++ b/next/src/layout/Dashboard/Header/HeaderContent/MobileSection.jsx @@ -1,3 +1,5 @@ +'use client'; + import { useEffect, useRef, useState } from 'react'; // material-ui @@ -86,7 +88,7 @@ export default function MobileSection() { ({ boxShadow: theme.customShadows.z1 })}> - + diff --git a/src/layout/Dashboard/Header/HeaderContent/Notification.jsx b/next/src/layout/Dashboard/Header/HeaderContent/Notification.jsx similarity index 99% rename from src/layout/Dashboard/Header/HeaderContent/Notification.jsx rename to next/src/layout/Dashboard/Header/HeaderContent/Notification.jsx index e4c5af052..f61c15f04 100644 --- a/src/layout/Dashboard/Header/HeaderContent/Notification.jsx +++ b/next/src/layout/Dashboard/Header/HeaderContent/Notification.jsx @@ -1,3 +1,5 @@ +'use client'; + import { useRef, useState } from 'react'; // material-ui diff --git a/src/layout/Dashboard/Header/HeaderContent/Profile/ProfileTab.jsx b/next/src/layout/Dashboard/Header/HeaderContent/Profile/ProfileTab.jsx similarity index 94% rename from src/layout/Dashboard/Header/HeaderContent/Profile/ProfileTab.jsx rename to next/src/layout/Dashboard/Header/HeaderContent/Profile/ProfileTab.jsx index 65a0b4a14..9439d06ae 100644 --- a/src/layout/Dashboard/Header/HeaderContent/Profile/ProfileTab.jsx +++ b/next/src/layout/Dashboard/Header/HeaderContent/Profile/ProfileTab.jsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; // material-ui import List from '@mui/material/List'; import ListItemButton from '@mui/material/ListItemButton'; @@ -51,5 +50,3 @@ export default function ProfileTab() { ); } - -ProfileTab.propTypes = { handleLogout: PropTypes.func }; diff --git a/src/layout/Dashboard/Header/HeaderContent/Profile/SettingTab.jsx b/next/src/layout/Dashboard/Header/HeaderContent/Profile/SettingTab.jsx similarity index 100% rename from src/layout/Dashboard/Header/HeaderContent/Profile/SettingTab.jsx rename to next/src/layout/Dashboard/Header/HeaderContent/Profile/SettingTab.jsx diff --git a/next/src/layout/Dashboard/Header/HeaderContent/Profile/index.jsx b/next/src/layout/Dashboard/Header/HeaderContent/Profile/index.jsx new file mode 100644 index 000000000..74f9ebf8e --- /dev/null +++ b/next/src/layout/Dashboard/Header/HeaderContent/Profile/index.jsx @@ -0,0 +1,184 @@ +'use client'; + +import PropTypes from 'prop-types'; +import { useRef, useState } from 'react'; + +// material-ui +import { useTheme } from '@mui/material/styles'; +import ButtonBase from '@mui/material/ButtonBase'; +import CardContent from '@mui/material/CardContent'; +import ClickAwayListener from '@mui/material/ClickAwayListener'; +import Paper from '@mui/material/Paper'; +import Popper from '@mui/material/Popper'; +import Stack from '@mui/material/Stack'; +import Tab from '@mui/material/Tab'; +import Tabs from '@mui/material/Tabs'; +import Tooltip from '@mui/material/Tooltip'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; + +// project imports +import ProfileTab from './ProfileTab'; +import SettingTab from './SettingTab'; +import Avatar from 'components/@extended/Avatar'; +import MainCard from 'components/MainCard'; +import Transitions from 'components/@extended/Transitions'; +import IconButton from 'components/@extended/IconButton'; + +// assets +import LogoutOutlined from '@ant-design/icons/LogoutOutlined'; +import SettingOutlined from '@ant-design/icons/SettingOutlined'; +import UserOutlined from '@ant-design/icons/UserOutlined'; +const avatar1 = '/assets/images/users/avatar-1.png'; + +// tab panel wrapper +function TabPanel({ children, value, index, ...other }) { + return ( + + ); +} + +function a11yProps(index) { + return { + id: `profile-tab-${index}`, + 'aria-controls': `profile-tabpanel-${index}` + }; +} + +// ==============================|| HEADER CONTENT - PROFILE ||============================== // + +export default function Profile() { + const theme = useTheme(); + + const anchorRef = useRef(null); + const [open, setOpen] = useState(false); + const handleToggle = () => { + setOpen((prevOpen) => !prevOpen); + }; + + const handleClose = (event) => { + if (anchorRef.current && anchorRef.current.contains(event.target)) { + return; + } + setOpen(false); + }; + + const [value, setValue] = useState(0); + + const handleChange = (event, newValue) => { + setValue(newValue); + }; + + return ( + + + ({ + p: 0.25, + borderRadius: 1, + '&:focus-visible': { outline: `2px solid ${theme.vars.palette.secondary.dark}`, outlineOffset: 2 } + })} + aria-label="open profile" + ref={anchorRef} + aria-controls={open ? 'profile-grow' : undefined} + aria-haspopup="true" + onClick={handleToggle} + > + + + + + {({ TransitionProps }) => ( + + ({ boxShadow: theme.vars.customShadows.z1, width: 290, minWidth: 240, maxWidth: { xs: 250, md: 290 } })}> + + + + + + + + John Doe + + UI/UX Designer + + + + + + + + + + + + + + } + label="Profile" + {...a11yProps(0)} + /> + } + label="Setting" + {...a11yProps(1)} + /> + + + + + + + + + + + + + )} + + + ); +} + +TabPanel.propTypes = { children: PropTypes.node, value: PropTypes.number, index: PropTypes.number, other: PropTypes.any }; diff --git a/src/layout/Dashboard/Header/HeaderContent/Search.jsx b/next/src/layout/Dashboard/Header/HeaderContent/Search.jsx similarity index 100% rename from src/layout/Dashboard/Header/HeaderContent/Search.jsx rename to next/src/layout/Dashboard/Header/HeaderContent/Search.jsx diff --git a/next/src/layout/Dashboard/Header/HeaderContent/index.jsx b/next/src/layout/Dashboard/Header/HeaderContent/index.jsx new file mode 100644 index 000000000..5a46faa16 --- /dev/null +++ b/next/src/layout/Dashboard/Header/HeaderContent/index.jsx @@ -0,0 +1,44 @@ +'use client'; + +// material-ui +import useMediaQuery from '@mui/material/useMediaQuery'; +import IconButton from '@mui/material/IconButton'; +import Link from '@mui/material/Link'; +import Box from '@mui/material/Box'; + +// project imports +import Search from './Search'; +import Profile from './Profile'; +import Notification from './Notification'; +import MobileSection from './MobileSection'; + +// project import +import { GithubOutlined } from '@ant-design/icons'; + +// ==============================|| HEADER - CONTENT ||============================== // + +export default function HeaderContent() { + const downLG = useMediaQuery((theme) => theme.breakpoints.down('lg')); + + return ( + <> + {!downLG && } + {downLG && } + + + + + + {!downLG && } + {downLG && } + + ); +} diff --git a/src/layout/Dashboard/Header/index.jsx b/next/src/layout/Dashboard/Header/index.jsx similarity index 100% rename from src/layout/Dashboard/Header/index.jsx rename to next/src/layout/Dashboard/Header/index.jsx diff --git a/next/src/layout/Dashboard/index.jsx b/next/src/layout/Dashboard/index.jsx new file mode 100644 index 000000000..7e0be380d --- /dev/null +++ b/next/src/layout/Dashboard/index.jsx @@ -0,0 +1,55 @@ +'use client'; + +import { useEffect } from 'react'; + +// material-ui +import useMediaQuery from '@mui/material/useMediaQuery'; +import Toolbar from '@mui/material/Toolbar'; +import Box from '@mui/material/Box'; + +// project imports +import Drawer from './Drawer'; +import Header from './Header'; +import Footer from './Footer'; +import Loader from 'components/Loader'; +import Breadcrumbs from 'components/@extended/Breadcrumbs'; + +import { handlerDrawerOpen, useGetMenuMaster } from 'api/menu'; + +// ==============================|| MAIN LAYOUT ||============================== // + +export default function DashboardLayout({ children }) { + const { menuMasterLoading } = useGetMenuMaster(); + const downXL = useMediaQuery((theme) => theme.breakpoints.down('xl')); + + // set media wise responsive drawer + useEffect(() => { + handlerDrawerOpen(!downXL); + }, [downXL]); + + if (menuMasterLoading) return ; + + return ( + +
+ + + + + + + {children} +