diff --git a/.env b/.env index 1eab438..9b7f89f 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -VITE_APP_VERSION=v4.0.0 +VITE_APP_VERSION=v4.1.0 GENERATE_SOURCEMAP=false PUBLIC_URL = https://codedthemes.com/demos/admin-templates/datta-able/react/free/ diff --git a/README.md b/README.md index 386ed68..03a3a3f 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Get started with a sweet set of features, including: - Bootstrap 5 - React Bootstrap -- React +- React 19.2 - npm/yarn package installer - Vite diff --git a/eslint.config.mjs b/eslint.config.mjs index 51aacd5..9dad347 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -53,21 +53,11 @@ export default [ 'jsx-a11y/label-has-associated-control': 'off', 'jsx-a11y/no-autofocus': 'off', - 'prettier/prettier': [ - 'warn', - { - bracketSpacing: true, - printWidth: 140, - singleQuote: true, - trailingComma: 'none', - tabWidth: 2, - useTabs: false - } - ] + 'prettier/prettier': 'warn', } }, { ignores: ['node_modules/**'], files: ['src/**/*.{js,jsx}'] } -]; +]; \ No newline at end of file diff --git a/index.html b/index.html index f96eb08..4ff17a1 100644 --- a/index.html +++ b/index.html @@ -1,34 +1,32 @@ - - - Welcome | Datta Able Free React Hooks + Admin Template - - - - - - - - - Datta Able React Admin Dashboard + + + Welcome | Datta Able Free React Hooks + Admin Template - - - - - - - -
- + + + + + + + + Datta Able React Admin Dashboard + + + + + + + + + +
+ - - - + + + + \ No newline at end of file diff --git a/jsconfig.app.json b/jsconfig.app.json index 316cf37..96e48b3 100644 --- a/jsconfig.app.json +++ b/jsconfig.app.json @@ -5,5 +5,7 @@ "moduleResolution": "Node", "allowSyntheticDefaultImports": true }, - "include": ["vite.config.mjs"] -} + "include": [ + "vite.config.mjs" + ] +} \ No newline at end of file diff --git a/jsconfig.json b/jsconfig.json index 3232afa..92573b5 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -2,7 +2,11 @@ "compilerOptions": { "target": "ESNext", "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], "allowJs": false, "skipLibCheck": true, "esModuleInterop": false, @@ -11,7 +15,6 @@ "module": "ESNext", "resolveJsonModule": true, "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, @@ -20,7 +23,6 @@ "noEmit": true, "jsx": "react-jsx", "baseUrl": "src", - /* Linting */ "strict": true, "noUnusedLocals": true, @@ -28,7 +30,19 @@ "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true }, - "exclude": ["node_modules", "vite.config.mjs"], // Exclude vite.config.mjs - "include": ["src", "**/*.js", "**/*.jsx", "src/**/*"], - "references": [{ "path": "./jsconfig.app.json" }] -} + "exclude": [ + "node_modules", + "vite.config.mjs" + ], // Exclude vite.config.mjs + "include": [ + "src", + "**/*.js", + "**/*.jsx", + "src/**/*" + ], + "references": [ + { + "path": "./jsconfig.app.json" + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index f023bda..23cfea7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "datta-able-free-react-admin-templete", - "version": "4.0.0", + "version": "4.1.0", "private": true, + "type": "module", "scripts": { "start": "vite", "build": "vite build", @@ -11,40 +12,39 @@ "preview": "vite preview" }, "dependencies": { - "@fontsource/open-sans": "5.2.5", - "@react-google-maps/api": "2.20.5", - "apexcharts": "4.5.0", - "axios": "1.7.9", - "bootstrap": "5.3.3", - "chance": "1.1.12", + "@fontsource/open-sans": "5.2.7", + "@react-google-maps/api": "2.20.7", + "apexcharts": "5.3.5", + "bootstrap": "5.3.8", + "chance": "1.1.13", "formik": "2.4.6", - "jsvectormap": "1.6.0", - "react": "18.2.0", - "react-apexcharts": "1.7.0", - "react-bootstrap": "2.10.6", + "jsvectormap": "1.7.0", + "react": "19.2.0", + "react-apexcharts": "1.8.0", + "react-bootstrap": "2.10.10", "react-device-detect": "2.2.3", - "react-dom": "18.2.0", - "react-hook-form": "7.54.2", - "react-router-dom": "7.0.2", - "simplebar-react": "3.3.0", - "swr": "2.3.2", - "vite": "6.0.2", + "react-dom": "19.2.0", + "react-hook-form": "7.65.0", + "react-router-dom": "7.9.5", + "simplebar-react": "3.3.2", + "swr": "2.3.6", + "vite": "7.1.12", "vite-jsconfig-paths": "2.0.1", - "yup": "1.6.1" + "yup": "1.7.1" }, "devDependencies": { - "@eslint/compat": "1.2.3", + "@eslint/compat": "1.4.1", "@eslint/eslintrc": "3.3.1", - "@eslint/js": "9.15.0", - "@vitejs/plugin-react": "4.3.4", - "eslint": "9.15.0", - "eslint-config-prettier": "9.1.0", + "@eslint/js": "9.38.0", + "@vitejs/plugin-react": "5.1.0", + "eslint": "9.38.0", + "eslint-config-prettier": "10.1.8", "eslint-plugin-jsx-a11y": "6.10.2", - "eslint-plugin-prettier": "5.2.1", + "eslint-plugin-prettier": "5.5.4", "eslint-plugin-react": "7.37.5", - "eslint-plugin-react-hooks": "5.0.0", - "prettier": "3.4.1", - "sass": "1.77.6" + "eslint-plugin-react-hooks": "7.0.1", + "prettier": "3.6.2", + "sass": "1.93.2" }, - "packageManager": "yarn@4.9.1" + "packageManager": "yarn@4.10.3" } diff --git a/src/assets/images/user/avatar-10.png b/src/assets/images/user/avatar-10.png deleted file mode 100644 index 7f8925f..0000000 Binary files a/src/assets/images/user/avatar-10.png and /dev/null differ diff --git a/src/assets/images/user/avatar-6.png b/src/assets/images/user/avatar-6.png deleted file mode 100644 index b099c2c..0000000 Binary files a/src/assets/images/user/avatar-6.png and /dev/null differ diff --git a/src/assets/images/user/avatar-7.png b/src/assets/images/user/avatar-7.png deleted file mode 100644 index 5f2c41a..0000000 Binary files a/src/assets/images/user/avatar-7.png and /dev/null differ diff --git a/src/assets/images/user/avatar-8.png b/src/assets/images/user/avatar-8.png deleted file mode 100644 index c6b8c3e..0000000 Binary files a/src/assets/images/user/avatar-8.png and /dev/null differ diff --git a/src/assets/images/user/avatar-9.png b/src/assets/images/user/avatar-9.png deleted file mode 100644 index a895039..0000000 Binary files a/src/assets/images/user/avatar-9.png and /dev/null differ diff --git a/src/assets/scss/settings/_bootstrap-variables.scss b/src/assets/scss/settings/_bootstrap-variables.scss index 53722f0..32a1e6b 100644 --- a/src/assets/scss/settings/_bootstrap-variables.scss +++ b/src/assets/scss/settings/_bootstrap-variables.scss @@ -15,7 +15,7 @@ $grays: ( '600': $gray-600, '700': $gray-700, '800': $gray-800, - '900': $gray-900 + '900': $gray-900, ); // fusv-enable @@ -34,7 +34,7 @@ $colors: ( 'black': $black, 'white': $white, 'gray': $gray-600, - 'gray-dark': $gray-800 + 'gray-dark': $gray-800, ); // scss-docs-end colors-map @@ -55,7 +55,7 @@ $theme-colors: ( 'warning': $warning, 'danger': $danger, 'light': $light, - 'dark': $dark + 'dark': $dark, ); // scss-docs-end theme-colors-map @@ -80,7 +80,7 @@ $blues: ( 'blue-600': $blue-600, 'blue-700': $blue-700, 'blue-800': $blue-800, - 'blue-900': $blue-900 + 'blue-900': $blue-900, ); $indigos: ( @@ -92,7 +92,7 @@ $indigos: ( 'indigo-600': $indigo-600, 'indigo-700': $indigo-700, 'indigo-800': $indigo-800, - 'indigo-900': $indigo-900 + 'indigo-900': $indigo-900, ); $purples: ( @@ -104,7 +104,7 @@ $purples: ( 'purple-600': $purple-600, 'purple-700': $purple-700, 'purple-800': $purple-800, - 'purple-900': $purple-900 + 'purple-900': $purple-900, ); $pinks: ( @@ -116,7 +116,7 @@ $pinks: ( 'pink-600': $pink-600, 'pink-700': $pink-700, 'pink-800': $pink-800, - 'pink-900': $pink-900 + 'pink-900': $pink-900, ); $reds: ( @@ -128,7 +128,7 @@ $reds: ( 'red-600': $red-600, 'red-700': $red-700, 'red-800': $red-800, - 'red-900': $red-900 + 'red-900': $red-900, ); $oranges: ( @@ -140,7 +140,7 @@ $oranges: ( 'orange-600': $orange-600, 'orange-700': $orange-700, 'orange-800': $orange-800, - 'orange-900': $orange-900 + 'orange-900': $orange-900, ); $yellows: ( @@ -152,7 +152,7 @@ $yellows: ( 'yellow-600': $yellow-600, 'yellow-700': $yellow-700, 'yellow-800': $yellow-800, - 'yellow-900': $yellow-900 + 'yellow-900': $yellow-900, ); $greens: ( @@ -164,7 +164,7 @@ $greens: ( 'green-600': $green-600, 'green-700': $green-700, 'green-800': $green-800, - 'green-900': $green-900 + 'green-900': $green-900, ); $teals: ( @@ -176,7 +176,7 @@ $teals: ( 'teal-600': $teal-600, 'teal-700': $teal-700, 'teal-800': $teal-800, - 'teal-900': $teal-900 + 'teal-900': $teal-900, ); $cyans: ( @@ -188,12 +188,18 @@ $cyans: ( 'cyan-600': $cyan-600, 'cyan-700': $cyan-700, 'cyan-800': $cyan-800, - 'cyan-900': $cyan-900 + 'cyan-900': $cyan-900, ); // fusv-enable // Characters which are escaped by the escape-svg function -$escaped-characters: (('<', '%3c'), ('>', '%3e'), ('#', '%23'), ('(', '%28'), (')', '%29')); +$escaped-characters: ( + ('<', '%3c'), + ('>', '%3e'), + ('#', '%23'), + ('(', '%28'), + (')', '%29') +); // Options // @@ -243,7 +249,7 @@ $spacers: ( 2: $spacer * 0.5, 3: $spacer, 4: $spacer * 1.5, - 5: $spacer * 3 + 5: $spacer * 3, ); // scss-docs-end spacer-variables-maps @@ -255,7 +261,7 @@ $spacers: ( $position-values: ( 0: 0, 50: 50%, - 100: 100% + 100: 100%, ); // scss-docs-end position-map @@ -265,6 +271,7 @@ $position-values: ( body { font-feature-settings: 'salt'; } + $body-bg: #f4f7fa; // change $body-color: #888; // change $body-text-align: null; @@ -300,7 +307,7 @@ $grid-breakpoints: ( md: 768px, lg: 992px, xl: 1200px, - xxl: 1400px + xxl: 1400px, ); // scss-docs-end grid-breakpoints @@ -317,7 +324,7 @@ $container-max-widths: ( md: 720px, lg: 960px, xl: 1140px, - xxl: 1320px + xxl: 1320px, ); // scss-docs-end container-max-widths @@ -347,7 +354,7 @@ $border-widths: ( 2: 2px, 3: 3px, 4: 4px, - 5: 5px + 5: 5px, ); $border-style: solid; @@ -390,7 +397,7 @@ $aspect-ratios: ( '1x1': 100%, '4x3': calc(3 / 4 * 100%), '16x9': calc(9 / 16 * 100%), - '21x9': calc(9 / 21 * 100%) + '21x9': calc(9 / 21 * 100%), ); // scss-docs-end aspect-ratios // stylelint-enable function-disallowed-list @@ -436,7 +443,7 @@ $font-sizes: ( 3: $h3-font-size, 4: $h4-font-size, 5: $h5-font-size, - 6: $h6-font-size + 6: $h6-font-size, ); // scss-docs-end font-sizes @@ -455,7 +462,7 @@ $display-font-sizes: ( 3: 4rem, 4: 3.5rem, 5: 3rem, - 6: 2.5rem + 6: 2.5rem, ); $display-font-weight: 300; @@ -549,7 +556,7 @@ $table-variants: ( 'warning': shift-color($warning, $table-bg-scale), 'danger': shift-color($danger, $table-bg-scale), 'light': $light, - 'dark': $dark + 'dark': $dark, ); // scss-docs-end table-variables @@ -601,8 +608,7 @@ $btn-font-size-lg: $input-btn-font-size-lg; $btn-border-width: $input-btn-border-width; $btn-font-weight: 500; -$btn-box-shadow: - inset 0 1px 0 rgba($white, 0.15), +$btn-box-shadow: inset 0 1px 0 rgba($white, 0.15), 0 1px 1px rgba($black, 0.075); $btn-focus-width: $input-btn-focus-width; $btn-focus-box-shadow: $input-btn-focus-box-shadow; @@ -619,8 +625,7 @@ $btn-border-radius: 4px; $btn-border-radius-sm: 2px; $btn-border-radius-lg: 6px; -$btn-transition: - color 0.15s ease-in-out, +$btn-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; @@ -693,12 +698,14 @@ $input-height-inner: add($input-line-height * 1em, calc($input-padding-y * 2)); $input-height-inner-half: add($input-line-height * 0.5em, $input-padding-y); $input-height-inner-quarter: add($input-line-height * 0.25em, calc($input-padding-y / 2)); -$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)); -$input-height-sm: add($input-line-height * 1em, add($input-padding-y-sm * 2, $input-height-border, false)); -$input-height-lg: add($input-line-height * 1em, add($input-padding-y-lg * 2, $input-height-border, false)); +$input-height: add($input-line-height * 1em, + add($input-padding-y * 2, $input-height-border, false)); +$input-height-sm: add($input-line-height * 1em, + add($input-padding-y-sm * 2, $input-height-border, false)); +$input-height-lg: add($input-line-height * 1em, + add($input-padding-y-lg * 2, $input-height-border, false)); -$input-transition: - border-color 0.15s ease-in-out, +$input-transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; $form-color-width: 3rem; @@ -710,8 +717,7 @@ $form-check-padding-start: $form-check-input-width + 0.5em; $form-check-margin-bottom: 0.125rem; $form-check-label-color: null; $form-check-label-cursor: null; -$form-check-transition: - background-color 0.15s ease-in-out, +$form-check-transition: background-color 0.15s ease-in-out, background-position 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; @@ -777,10 +783,8 @@ $form-select-bg-size: 16px 12px; // In pixels because image dimensions $form-select-indicator-color: $gray-800; $form-select-indicator: url("data:image/svg+xml,"); -$form-select-feedback-icon-padding-end: add( - 1em * 0.75, - (2 * $form-select-padding-y * 0.75) + $form-select-padding-x + $form-select-indicator-padding -); +$form-select-feedback-icon-padding-end: add(1em * 0.75, + (2 * $form-select-padding-y * 0.75) + $form-select-padding-x + $form-select-indicator-padding); $form-select-feedback-icon-position: center right ($form-select-padding-x + $form-select-indicator-padding); $form-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half; @@ -820,14 +824,12 @@ $form-range-thumb-bg: $component-active-bg; $form-range-thumb-border: 0; $form-range-thumb-border-radius: 1rem; $form-range-thumb-box-shadow: 0 0.1rem 0.25rem rgba($black, 0.1); -$form-range-thumb-focus-box-shadow: - 0 0 0 1px $body-bg, +$form-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow; $form-range-thumb-focus-box-shadow-width: $input-focus-width; // For focus box shadow issue in Edge $form-range-thumb-active-bg: rgba(#{var(--bs-primary-rgb)}, 0.8); $form-range-thumb-disabled-bg: $gray-500; -$form-range-thumb-transition: - background-color 0.15s ease-in-out, +$form-range-thumb-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; @@ -860,14 +862,12 @@ $form-feedback-icon-invalid: url("data:image/svg+xml,") + ); + } + + &[type='radio'] { + background-image: escape-svg( + url("data:image/svg+xml,") + ); + } + } + } + + &.input-primary, + &.input-light-primary { &:focus { &[type='checkbox'], &[type='radio'] { @@ -107,7 +136,7 @@ $i: 1; } &.form-switch { - .form-check-input { + .form-check-input.input-light-primary { &:checked { background-image: escape-svg( url("data:image/svg+xml,") @@ -194,7 +223,7 @@ $i: 1; } $drp-icon: ( - 'preset-1': '\ea61' + 'preset-1': '\ea61', ); @each $key, $value in $drp-icon { [data-pc-drp-menu-icon='#{$key}'] { @@ -203,7 +232,7 @@ $drp-icon: ( } $drp-link-icon: ( - 'preset-1': '' + 'preset-1': '', ); @each $key, $value in $drp-link-icon { [data-pc-drp-menu-link-icon='#{$key}'] { diff --git a/src/assets/scss/style.scss b/src/assets/scss/style.scss index 4e04bbb..e204345 100644 --- a/src/assets/scss/style.scss +++ b/src/assets/scss/style.scss @@ -24,6 +24,9 @@ File: style.css ========================================================================= =================================================================================== */ +@use 'sass:map'; +@use 'sass:color'; +@use 'sass:string'; // main framework @import 'bootstrap/scss/functions'; @@ -89,6 +92,7 @@ File: style.css @import 'themes/components/progress'; @import 'themes/components/table'; @import 'themes/components/form'; +@import 'themes/components/widget'; // pages -@import 'themes/pages/authentication'; +@import 'themes/pages/authentication'; \ No newline at end of file diff --git a/src/assets/scss/themes/_general.scss b/src/assets/scss/themes/_general.scss index 9a14845..a3d295e 100644 --- a/src/assets/scss/themes/_general.scss +++ b/src/assets/scss/themes/_general.scss @@ -85,6 +85,11 @@ strong { border-radius: 50%; } +.apexcharts-tooltip-series-group.apexcharts-active, +.apexcharts-tooltip-series-group:last-child { + padding-bottom: 0 !important; +} + /* ================================ Blockquote Start ===================== */ @media (min-width: 1600px) { diff --git a/src/assets/scss/themes/_generic.scss b/src/assets/scss/themes/_generic.scss index 2930eb1..3233505 100644 --- a/src/assets/scss/themes/_generic.scss +++ b/src/assets/scss/themes/_generic.scss @@ -2,6 +2,8 @@ 1. Generic-class css start ========================== **/ /*====== Padding , Margin css starts ======*/ +@use 'sass:map'; + $i: 0; @while $i<=50 { .p { @@ -153,7 +155,7 @@ $social-colors: ( 'behance': #0057ff, 'dropbox': #3380ff, 'linkedin': #0077b5, - 'amazon': #000000 + 'amazon': #000000, ); @each $color, $value in $social-colors { .bg-light-#{$color} { @@ -292,7 +294,7 @@ $more-colors: ( 600: $blue-600, 700: $blue-700, 800: $blue-800, - 900: $blue-900 + 900: $blue-900, ), 'purple': ( 100: $purple-100, @@ -303,7 +305,7 @@ $more-colors: ( 600: $purple-600, 700: $purple-700, 800: $purple-800, - 900: $purple-900 + 900: $purple-900, ), 'pink': ( 100: $pink-100, @@ -314,7 +316,7 @@ $more-colors: ( 600: $pink-600, 700: $pink-700, 800: $pink-800, - 900: $pink-900 + 900: $pink-900, ), 'red': ( 100: $red-100, @@ -325,7 +327,7 @@ $more-colors: ( 600: $red-600, 700: $red-700, 800: $red-800, - 900: $red-900 + 900: $red-900, ), 'orange': ( 100: $orange-100, @@ -336,7 +338,7 @@ $more-colors: ( 600: $orange-600, 700: $orange-700, 800: $orange-800, - 900: $orange-900 + 900: $orange-900, ), 'yellow': ( 100: $yellow-100, @@ -347,7 +349,7 @@ $more-colors: ( 600: $yellow-600, 700: $yellow-700, 800: $yellow-800, - 900: $yellow-900 + 900: $yellow-900, ), 'green': ( 100: $green-100, @@ -358,7 +360,7 @@ $more-colors: ( 600: $green-600, 700: $green-700, 800: $green-800, - 900: $green-900 + 900: $green-900, ), 'cyan': ( 100: $cyan-100, @@ -369,7 +371,7 @@ $more-colors: ( 600: $cyan-600, 700: $cyan-700, 800: $cyan-800, - 900: $cyan-900 + 900: $cyan-900, ), 'gray': ( 100: $gray-100, @@ -380,8 +382,8 @@ $more-colors: ( 600: $gray-600, 700: $gray-700, 800: $gray-800, - 900: $gray-900 - ) + 900: $gray-900, + ), ); @each $name, $value in $more-colors { $i: 100; @@ -399,7 +401,7 @@ $more-colors: ( @each $name, $value in $more-colors { $i: 100; @while $i<=900 { - $temp: map-get($value, $i); + $temp: map.get($value, $i); --bs-#{$name}-#{$i}: #{$temp}; $i: $i + 100; } diff --git a/src/assets/scss/themes/components/_table.scss b/src/assets/scss/themes/components/_table.scss index 59f137d..7d7939f 100644 --- a/src/assets/scss/themes/components/_table.scss +++ b/src/assets/scss/themes/components/_table.scss @@ -1,6 +1,7 @@ // ============================ // 17. Table css start // ============================ +@use 'sass:color'; .table { &.table-align-center { @@ -11,9 +12,18 @@ } thead th { padding: 0.9rem 0.75rem; + border-bottom: 1px solid #f1f1f1; + font-size: 13px; + color: #262626; + background: rgba(244, 247, 250, 0.5); + text-transform: uppercase; } td, th { + border-top: 1px solid $border-color; + border-bottom: none; + white-space: nowrap; + padding: 0.7rem 0.75rem; vertical-align: middle; } &.table-borderless { @@ -25,7 +35,7 @@ } .table-hover tbody tr:hover { - background-color: transparentize($primary, 0.97); + background-color: #{color.adjust($primary, $alpha: -0.97)}; } // ============================ diff --git a/src/assets/scss/themes/components/_widget.scss b/src/assets/scss/themes/components/_widget.scss new file mode 100644 index 0000000..bc7370e --- /dev/null +++ b/src/assets/scss/themes/components/_widget.scss @@ -0,0 +1,134 @@ +// ============================ +// 10. Widget css start +// ============================ + +.Recent-Users { + .table { + tr { + &:first-child { + td { + border-top: 0; + } + } + } + + td { + vertical-align: middle; + } + + .label { + border-radius: 15px; + box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.2); + } + } +} + +.card-social { + &:hover { + i { + font-size: 40px; + transition: all 0.3s ease-in-out; + } + } + + .progress { + height: 6px; + } + + .card-active > div + div { + border-left: 1px solid var(--bs-border-color); + } +} + +.card { + .card-body { + code { + background-color: #eee; + margin: 5px; + display: inline-block; + border-radius: 3px; + padding: 0 3px; + } + } +} + +/* ================ new css =================== */ + +.table-card .card-body, +.table-body.card-body { + padding-left: 0; + padding-right: 0; + padding-top: 0; + + .table { + > thead > tr > th { + border-top: 0; + } + + tr { + td, + th { + &:first-child { + padding-left: 25px; + + @include media-breakpoint-down(sm) { + padding-left: 20px; + } + } + + &:last-child { + padding-right: 25px; + + @include media-breakpoint-down(sm) { + padding-right: 20px; + } + } + } + } + + &.without-header { + tr { + &:first-child { + td { + border-top: none; + } + } + } + } + } +} + +.table-card { + .row-table { + display: flex; + align-items: center; + table-layout: fixed; + height: 100%; + width: 100%; + margin: 0; + + svg { + margin: 0 20px; + } + + i { + padding: 50px 20px; + } + + > [class*='col-'] { + display: table-cell; + float: none; + table-layout: fixed; + vertical-align: middle; + + .row { + display: flex; + align-items: center; + } + } + } +} + +// ============================ +// 10. Widget css end +// ============================ diff --git a/src/assets/scss/themes/layouts/_pc-common.scss b/src/assets/scss/themes/layouts/_pc-common.scss index 5a83764..2632f6e 100644 --- a/src/assets/scss/themes/layouts/_pc-common.scss +++ b/src/assets/scss/themes/layouts/_pc-common.scss @@ -13,6 +13,7 @@ padding-left: 40px; padding-right: 40px; padding-top: 20px; + @include media-breakpoint-down(xl) { &.container { max-width: 100%; @@ -20,11 +21,11 @@ } } - .page-header + .row { + .page-header+.row { padding-top: 24px; } - .page-header + .pc-content { + .page-header+.pc-content { padding-top: calc(30px + 55px); } } @@ -69,7 +70,7 @@ color: $primary; } - + .breadcrumb-item::before { + +.breadcrumb-item::before { position: relative; top: 2px; } @@ -92,8 +93,8 @@ margin-left: 0px; margin-right: 0px; - .page-header + .row, - .page-header + .pc-content, + .page-header+.row, + .page-header+.pc-content, .pc-content { padding-top: 20px; padding-left: 15px; @@ -115,6 +116,7 @@ padding-top: 0; padding-bottom: 5px; } + .pc-container { .pc-content { padding: 15px; @@ -125,8 +127,8 @@ padding-right: 0; } - .page-header + .row, - .page-header + .pc-content { + .page-header+.row, + .page-header+.pc-content { padding-left: 0; padding-right: 0; } @@ -156,29 +158,7 @@ position: relative; } -.simplebar-scrollbar { - position: absolute; - left: 0; - right: 0; - min-height: 10px; - - &:before { - position: absolute; - content: ''; - background: darken($body-bg, 25%); - border-radius: 7px; - left: 2px; - right: 2px; - opacity: 0; - transition: opacity 0.2s linear; - } - - &.simplebar-visible:before { - opacity: 0.5; - transition: opacity 0s linear; - } -} // ============================ // 6. Common layout css end -// ============================ +// ============================ \ No newline at end of file diff --git a/src/assets/scss/themes/layouts/_pc-sidebar.scss b/src/assets/scss/themes/layouts/_pc-sidebar.scss index 4a94091..1feb987 100644 --- a/src/assets/scss/themes/layouts/_pc-sidebar.scss +++ b/src/assets/scss/themes/layouts/_pc-sidebar.scss @@ -16,6 +16,7 @@ svg { stroke: #fff; } + i { color: #fff; } @@ -105,7 +106,7 @@ vertical-align: text-top; } - > svg { + >svg { width: 18px; height: 18px; display: inline-block; @@ -130,10 +131,10 @@ } } - .pc-navbar > .pc-item:not(.pc-caption) { + .pc-navbar>.pc-item:not(.pc-caption) { position: relative; - > .pc-link { + >.pc-link { &::after { content: ''; position: absolute; @@ -163,7 +164,7 @@ background: var(--pc-sidebar-active-color); } - > .pc-link { + >.pc-link { font-weight: 500; color: var(--pc-sidebar-main-active-color); @@ -175,8 +176,9 @@ .pc-submenu { padding: 15px 0; + .pc-item { - > .pc-link { + >.pc-link { &:after { content: ''; border-radius: 50%; @@ -192,7 +194,7 @@ &.pc-trigger, &.active { - > .pc-link { + >.pc-link { font-weight: 500; color: var(--pc-sidebar-active-color); @@ -206,7 +208,7 @@ &.active, &:focus, &:hover { - > .pc-link { + >.pc-link { &:after { opacity: 1; transform: scale(1.2); @@ -274,7 +276,7 @@ display: inline-block; transition: all 0.2s ease-in-out; - > svg { + >svg { width: 14px; height: 14px; } @@ -320,18 +322,18 @@ .pc-hasmenu { &:not(.pc-trigger) { - > .pc-submenu { + >.pc-submenu { display: none; } } &.pc-trigger { - > .pc-submenu { + >.pc-submenu { display: block; } - > .pc-link { - > .pc-arrow { + >.pc-link { + >.pc-arrow { transform: rotate(90deg); } } @@ -354,12 +356,12 @@ .pc-sidebar { transition: width 0.2s ease; - ~ .pc-header { + ~.pc-header { transition: left 0.2s ease; } - ~ .pc-footer, - ~ .pc-container { + ~.pc-footer, + ~.pc-container { transition: margin-left 0.2s ease; } @@ -367,12 +369,12 @@ width: 0; --pc-sidebar-border: none; - ~ .pc-header { + ~.pc-header { left: 0; } - ~ .pc-footer, - ~ .pc-container { + ~.pc-footer, + ~.pc-container { margin-left: 0px; } } @@ -408,4 +410,4 @@ // ============================ // 3. Sidebar css end -// ============================ +// ============================ \ No newline at end of file diff --git a/src/branding.json b/src/branding.json index d139f9c..e5be606 100644 --- a/src/branding.json +++ b/src/branding.json @@ -3,6 +3,7 @@ "title": "React Admin Dashboard Template", "Docs": "https://codedthemes.gitbook.io/datta", "changeLog": "https://codedthemes.gitbook.io/datta/changelog", + "buyNow": "https://codedthemes.com/item/datta-able-react-admin-template/", "company": { "name": "CodedThemes", "url": "https://codedthemes.com", diff --git a/src/components/MainCard.jsx b/src/components/MainCard.jsx index 766572a..7945ba4 100644 --- a/src/components/MainCard.jsx +++ b/src/components/MainCard.jsx @@ -1,4 +1,4 @@ -import { forwardRef } from 'react'; +import PropTypes from 'prop-types'; // react-bootstrap import Card from 'react-bootstrap/Card'; @@ -6,46 +6,51 @@ import Stack from 'react-bootstrap/Stack'; // ==============================|| MAIN CARD ||============================== // -const MainCard = forwardRef( - ( - { - children, - subheader, - footer, - secondary, - content = true, - codeString, - title, - className, - headerClassName, - bodyClassName, - footerClassName - }, - ref - ) => { - return ( - - {/* Header Section */} - {title && ( - - - - {typeof title === 'string' ?
{title}
: title} - {subheader && {subheader}} -
- {secondary} +export default function MainCard({ + children, + subheader, + footer, + secondary, + content = true, + title, + className, + headerClassName, + bodyClassName, + footerClassName, + ref +}) { + return ( + + {/* Header Section */} + {title && ( + + + + {typeof title === 'string' ?
{title}
: title} + {subheader && {subheader}}
-
- )} - {/* Content */} - {content && {children}} - {!content && children} - {/* Footer Section for Code Highlighting */} - {codeString &&
} - {footer && {footer}} -
- ); - } -); + {secondary} +
+
+ )} + {/* Content */} + {content && {children}} + {!content && children} + {footer && {footer}} +
+ ); +} -export default MainCard; +MainCard.propTypes = { + children: PropTypes.node, + subheader: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + footer: PropTypes.node, + secondary: PropTypes.node, + content: PropTypes.bool, + title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + className: PropTypes.string, + headerClassName: PropTypes.string, + bodyClassName: PropTypes.string, + footerClassName: PropTypes.string, + ref: PropTypes.object +}; diff --git a/src/components/cards/SalesPerformanceCard.jsx b/src/components/cards/dashboard/SalesPerformanceCard.jsx similarity index 100% rename from src/components/cards/SalesPerformanceCard.jsx rename to src/components/cards/dashboard/SalesPerformanceCard.jsx diff --git a/src/components/cards/SocialStatsCard.jsx b/src/components/cards/dashboard/SocialStatsCard.jsx similarity index 100% rename from src/components/cards/SocialStatsCard.jsx rename to src/components/cards/dashboard/SocialStatsCard.jsx diff --git a/src/components/cards/StatIndicatorCard.jsx b/src/components/cards/dashboard/StatIndicatorCard.jsx similarity index 99% rename from src/components/cards/StatIndicatorCard.jsx rename to src/components/cards/dashboard/StatIndicatorCard.jsx index 7586a17..38c126c 100644 --- a/src/components/cards/StatIndicatorCard.jsx +++ b/src/components/cards/dashboard/StatIndicatorCard.jsx @@ -1,4 +1,5 @@ import PropTypes from 'prop-types'; + // react-bootstrap import Stack from 'react-bootstrap/Stack'; diff --git a/src/components/third-party/SimpleBar.jsx b/src/components/third-party/SimpleBar.jsx index 749e7f2..5c9d86e 100644 --- a/src/components/third-party/SimpleBar.jsx +++ b/src/components/third-party/SimpleBar.jsx @@ -6,10 +6,10 @@ import SimpleBar from 'simplebar-react'; // ==============================|| SIMPLE BAR SCROLL ||============================== // -export default function SimpleBarScroll({ children, className, style, ...other }) { +export default function SimpleBarScroll({ children, className, browserStyle, style, ...other }) { return ( <> - + {children} @@ -24,4 +24,10 @@ export default function SimpleBarScroll({ children, className, style, ...other } ); } -SimpleBarScroll.propTypes = { children: PropTypes.node, className: PropTypes.string, style: PropTypes.any, other: PropTypes.any }; +SimpleBarScroll.propTypes = { + children: PropTypes.node, + className: PropTypes.string, + browserStyle: PropTypes.any, + style: PropTypes.any, + other: PropTypes.any +}; diff --git a/src/config.js b/src/config.js index 7c64649..cc79fe1 100644 --- a/src/config.js +++ b/src/config.js @@ -3,30 +3,5 @@ export const APP_DEFAULT_PATH = '/dashboard/default'; export const DRAWER_WIDTH = 260; -export let MenuOrientation; - -(function (MenuOrientation) { - MenuOrientation['VERTICAL'] = 'vertical'; -})(MenuOrientation || (MenuOrientation = {})); - -// ==============================|| THEME CONFIG ||============================== // - -const config = { - fontFamily: `'Public Sans', sans-serif`, - i18n: 'en', - menuOrientation: MenuOrientation.VERTICAL, - container: false, - presetColor: 'default', - caption: true, - sidebarTheme: false, - customColor: 'preset-1', - headerColor: '', - navbarColor: '', - logoColor: '', - navbarCaptionColor: '', - navbarImg: '', - menuIcon: 'preset-1', - menuLinkIcon: 'preset-1' -}; export default config; diff --git a/src/global.scss b/src/global.scss index 5e11c05..4d8eb57 100644 --- a/src/global.scss +++ b/src/global.scss @@ -1,10 +1,14 @@ +@use 'sass:color'; + .bg-light[colspan] { @include media-breakpoint-up(sm) { padding-left: 40px; } + @include media-breakpoint-up(md) { padding-left: 48px; } + @include media-breakpoint-up(lg) { padding-left: 96px; } @@ -20,3 +24,29 @@ top: 16px; height: 0; } + +.apexcharts-tooltip-text-goals-value, +.apexcharts-tooltip-text-y-value, +.apexcharts-tooltip-text-z-value { + margin-left: 0 !important; +} + +.apexcharts-tooltip-series-group.apexcharts-active .apexcharts-tooltip-marker { + margin-bottom: 3px; +} + +.apexcharts-menu-item:hover { + background: #eee; +} + +.apexcharts-canvas .apexcharts-svg { + background: transparent !important; +} + +[data-pc-theme='dark'] { + + .apexcharts-menu-item:hover { + color: var(--bs-black); + } + +} \ No newline at end of file diff --git a/src/index.scss b/src/index.scss index 6ba14ac..038b3c1 100644 --- a/src/index.scss +++ b/src/index.scss @@ -2,8 +2,9 @@ @import './assets/fonts/tabler-icons.min.css'; @import 'simplebar-react/dist/simplebar.min.css'; +@import 'jsvectormap/dist/jsvectormap.css'; @import './assets/scss/themes/plugins/scrollbar'; @import 'assets/scss/style.scss'; @import 'assets/scss/style-preset.scss'; -@import './global.scss'; +@import './global.scss'; \ No newline at end of file diff --git a/src/layout/Auth/index.jsx b/src/layout/Auth/index.jsx index 089cf11..4308080 100644 --- a/src/layout/Auth/index.jsx +++ b/src/layout/Auth/index.jsx @@ -3,6 +3,13 @@ import { Outlet } from 'react-router-dom'; // project-imports import Loader from 'components/Loader'; +/** + * AuthLayout is a top-level component that wraps around the component + * from react-router-dom. It is used to set the page type of the application + * and renders the Configuration component (which is used to set the page title). + * + * @returns {React.ReactElement} The AuthLayout component. + */ // ==============================|| LAYOUT - AUTH ||============================== // diff --git a/src/layout/Dashboard/Drawer/DrawerContent/NavCollapse.jsx b/src/layout/Dashboard/Drawer/DrawerContent/NavCollapse.jsx new file mode 100644 index 0000000..b0aad78 --- /dev/null +++ b/src/layout/Dashboard/Drawer/DrawerContent/NavCollapse.jsx @@ -0,0 +1,169 @@ +import PropTypes from 'prop-types'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { Link, matchPath, useLocation, useNavigate } from 'react-router-dom'; + +// react-bootstrap +import Badge from 'react-bootstrap/Badge'; +import Collapse from 'react-bootstrap/Collapse'; +import ListGroup from 'react-bootstrap/ListGroup'; + +// project-imports +import NavItem from './NavItem'; +import { useGetMenuMaster } from 'api/menu'; + +// ==============================|| NAVIGATION - COLLAPSE ||============================== // + +export default function NavCollapse({ menu, level, parentId, setSelectedItems, selectedItems, setSelectedLevel, selectedLevel }) { + const { menuMaster } = useGetMenuMaster(); + const navigation = useNavigate(); + const drawerOpen = menuMaster?.isDashboardDrawerOpened; + + const [open, setOpen] = useState(false); + const [selected, setSelected] = useState(null); + const { pathname } = useLocation(); + + const isMenuActive = useCallback((menu, currentPath) => { + if (menu.type === 'item') { + return menu.url === currentPath; + } + if (menu.type === 'collapse' && Array.isArray(menu.children)) { + return menu.children.some((child) => isMenuActive(child, currentPath)); + } + return false; + }, []); + + const handleClick = (isRedirect) => { + setSelectedLevel(level); + const willOpen = !open; + setOpen(willOpen); + setSelected(willOpen ? menu.id : null); + setSelectedItems(willOpen ? menu : undefined); + if (menu.url && isRedirect) navigation(menu.url); + }; + + useEffect(() => { + if (selected === selectedItems?.id) { + if (level === 1) { + setOpen(true); + } + } else { + if (level === selectedLevel) { + setOpen(false); + + if (drawerOpen) { + setSelected(null); + } + } + } + }, [selectedItems, level, selected, drawerOpen, selectedLevel]); + + useEffect(() => { + if (pathname === menu.url) { + setSelected(menu.id); + } + }, [pathname, menu.id, menu.url]); + + const checkOpenForParent = useCallback( + (child, id) => { + child.forEach((item) => { + if (item.url === pathname) { + setOpen(true); + setSelected(id); + } + }); + }, + [pathname] + ); + + // menu collapse for sub-levels + useEffect(() => { + setOpen(false); + if (!menu.children) return; + + for (const item of menu.children) { + if (item.children?.length) { + checkOpenForParent(item.children, menu.id); + } + + if (item.link && matchPath({ path: item?.link, end: false }, pathname)) { + setSelected(menu.id); + setOpen(true); + break; + } + + if (item.url === pathname) { + setSelected(menu.id); + setOpen(true); + break; + } + } + }, [pathname, menu.id, menu.children, checkOpenForParent]); + + useEffect(() => { + if (menu.url === pathname) { + setSelected(menu.id); + setOpen(true); + } + }, [pathname, menu.url, menu.id]); + const navCollapse = useMemo( + () => + menu.children?.map((item) => { + switch (item.type) { + case 'collapse': + return ( + + ); + case 'item': + return ; + default: + return ( +
+ Fix - Collapse or Item +
+ ); + } + }) ?? [], + [menu.children, setSelectedItems, setSelectedLevel, selectedLevel, selectedItems, level, parentId] + ); + + return ( + + handleClick(true)}> + {menu.icon && ( + + + + )} + {menu.title} + + + + {menu.badge && {menu.badge}} + + +
+
    {navCollapse}
+
+
+
+ ); +} + +NavCollapse.propTypes = { + menu: PropTypes.any, + level: PropTypes.number, + parentId: PropTypes.string, + setSelectedItems: PropTypes.oneOfType([PropTypes.func, PropTypes.any]), + selectedItems: PropTypes.any, + setSelectedLevel: PropTypes.func, + selectedLevel: PropTypes.number +}; diff --git a/src/layout/Dashboard/Drawer/DrawerContent/NavGroup.jsx b/src/layout/Dashboard/Drawer/DrawerContent/NavGroup.jsx new file mode 100644 index 0000000..6992473 --- /dev/null +++ b/src/layout/Dashboard/Drawer/DrawerContent/NavGroup.jsx @@ -0,0 +1,93 @@ +import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'; +import { matchPath, useLocation } from 'react-router-dom'; + +// project-imports +import NavItem from './NavItem'; +import NavCollapse from './NavCollapse'; + +// ==============================|| NAVIGATION - GROUP ||============================== // + +export default function NavGroup(props) { + const { item, lastItem, remItems, lastItemId, setSelectedID, setSelectedItems, selectedItems, setSelectedLevel, selectedLevel } = props; + + const { pathname } = useLocation(); + const [currentItem, setCurrentItem] = useState(item); + + // Combine items if this is the last grouped item + useEffect(() => { + if (lastItem && item.id === lastItemId) { + const children = remItems.flatMap((ele) => ele.children ?? []); + setCurrentItem({ ...item, children }); + } else { + setCurrentItem(item); + } + }, [item, lastItem, lastItemId, remItems]); + + // Helper: Recursively check if route matches + const findMatchingChild = useCallback( + (children, parentId) => { + children.forEach((child) => { + if (child.children?.length) findMatchingChild(child.children, parentId); + const path = child.link || child.url; + if (path && matchPath({ path, end: true }, pathname)) { + setSelectedID(parentId); + } + }); + }, + [pathname, setSelectedID] + ); + + // On-load selection + useEffect(() => { + const children = currentItem.children ?? []; + children.forEach((child) => { + if (child.children?.length) findMatchingChild(child.children, currentItem.id); + const path = child.link || child.url; + if (path && matchPath({ path, end: true }, pathname)) { + setSelectedID(currentItem.id); + } + }); + }, [pathname, currentItem, findMatchingChild, setSelectedID]); + + // Memoized children render + const navCollapse = useMemo(() => { + if (!currentItem.children) return null; + + return currentItem.children.map((menuItem, index) => { + const key = menuItem.id || `${menuItem.type}-${index}`; + + switch (menuItem.type) { + case 'collapse': + return ( + + ); + case 'item': + return ; + default: + return ( +
+ Fix - Group Collapse or Items +
+ ); + } + }); + }, [currentItem, selectedItems, selectedLevel, setSelectedItems, setSelectedLevel]); + + return ( + +
  • + +
  • + {navCollapse} +
    + ); +} diff --git a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavItem.jsx b/src/layout/Dashboard/Drawer/DrawerContent/NavItem.jsx similarity index 52% rename from src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavItem.jsx rename to src/layout/Dashboard/Drawer/DrawerContent/NavItem.jsx index cb94ff6..f97c613 100644 --- a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavItem.jsx +++ b/src/layout/Dashboard/Drawer/DrawerContent/NavItem.jsx @@ -8,28 +8,28 @@ import { handlerDrawerOpen } from 'api/menu'; export default function NavItem({ item }) { const { pathname } = useLocation(); - const itemPath = item?.link || item?.url; - let itemTarget = '_self'; - if (item.target) { - itemTarget = '_blank'; - } + const itemPath = item?.link || item?.url; + const itemTarget = item?.target ? '_blank' : '_self'; const isSelected = itemPath ? !!matchPath({ path: itemPath, end: true }, pathname) : false; + const isMobile = window.innerWidth <= 1024; + + const handleClick = () => { + // close drawer on mobile + if (isMobile) handlerDrawerOpen(false); + }; + + const renderIcon = () => + item?.icon && ( + + + + ); + return ( -
  • - { - handlerDrawerOpen(false); - }} - > - {item?.icon && ( - - - - )} +
  • + + {renderIcon()} {item.title}
  • diff --git a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavCollapse.jsx b/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavCollapse.jsx deleted file mode 100644 index 0586c81..0000000 --- a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavCollapse.jsx +++ /dev/null @@ -1,166 +0,0 @@ -import PropTypes from 'prop-types'; -import { useEffect, useState, useMemo, useCallback } from 'react'; -import { matchPath, useLocation, useNavigate } from 'react-router-dom'; - -// react-bootstrap -import Badge from 'react-bootstrap/Badge'; -import ListGroup from 'react-bootstrap/ListGroup'; - -// project-imports -import NavItem from './NavItem'; -import { useGetMenuMaster } from 'api/menu'; - -// ==============================|| NAVIGATION - COLLAPSE ||============================== // - -export default function NavCollapse({ menu, level, parentId, setSelectedItems, selectedItems, setSelectedLevel, selectedLevel }) { - const { menuMaster } = useGetMenuMaster(); - const navigation = useNavigate(); - const drawerOpen = menuMaster?.isDashboardDrawerOpened; - - const [open, setOpen] = useState(false); - const [selected, setSelected] = useState(null); - const location = useLocation(); - - const isMenuActive = (menu, currentPath) => { - if (menu.type === 'item') { - return menu.url === currentPath; - } - if (menu.type === 'collapse' && Array.isArray(menu.children)) { - return menu.children.some((child) => isMenuActive(child, currentPath)); - } - return false; - }; - - const handleClick = (isRedirect) => { - const isMobile = window.innerWidth <= 1024; - setSelectedLevel(level); - - if (isMobile || !drawerOpen) { - setOpen(!open); - setSelected(!selected ? menu.id : null); - setSelectedItems(!selected ? menu : selectedItems); - if (menu.url && isRedirect) navigation(`${menu.url}`); - } - }; - - useMemo(() => { - if (selected === selectedItems?.id) { - if (level === 1) { - setOpen(true); - } - } else { - if (level === selectedLevel) { - setOpen(false); - - if (drawerOpen) { - setSelected(null); - } - } - } - }, [selectedItems, level, selected, drawerOpen, selectedLevel]); - - const { pathname } = useLocation(); - - useEffect(() => { - if (pathname === menu.url) { - setSelected(menu.id); - } - }, [pathname, menu.id, menu.url]); - - const checkOpenForParent = useCallback( - (child, id) => { - child.forEach((item) => { - if (item.url === pathname) { - setOpen(true); - setSelected(id); - } - }); - }, - [pathname] - ); - - // menu collapse for sub-levels - useEffect(() => { - setOpen(false); - if (menu.children) { - menu.children.forEach((item) => { - if (item.children?.length) { - checkOpenForParent(item.children, menu.id); - } - - if (item.link && !!matchPath({ path: item?.link, end: false }, pathname)) { - setSelected(menu.id); - setOpen(true); - } - - if (item.url === pathname) { - setSelected(menu.id); - setOpen(true); - } - }); - } - }, [pathname, menu.id, menu.children, checkOpenForParent]); - - useEffect(() => { - if (menu.url === pathname) { - setSelected(menu.id); - setOpen(true); - } - }, [pathname, menu]); - - const navCollapse = menu.children?.map((item) => { - switch (item.type) { - case 'collapse': - return ( - - ); - case 'item': - return ; - default: - return ( -
    - Fix - Collapse or Item -
    - ); - } - }); - - return ( - <> - - handleClick(true)}> - {menu.icon && ( - - - - )} - {menu.title} - - - - {menu.badge && {menu.badge}} - - {open === true &&
      {navCollapse}
    } -
    - - ); -} - -NavCollapse.propTypes = { - menu: PropTypes.any, - level: PropTypes.number, - parentId: PropTypes.string, - setSelectedItems: PropTypes.oneOfType([PropTypes.func, PropTypes.any]), - selectedItems: PropTypes.any, - setSelectedLevel: PropTypes.func, - selectedLevel: PropTypes.number -}; diff --git a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavGroup.jsx b/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavGroup.jsx deleted file mode 100644 index c7dd044..0000000 --- a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/NavGroup.jsx +++ /dev/null @@ -1,129 +0,0 @@ -import PropTypes from 'prop-types'; -import { Fragment, useCallback, useEffect, useState } from 'react'; -import { matchPath, useLocation } from 'react-router-dom'; - -// project-imports -import NavItem from './NavItem'; -import NavCollapse from './NavCollapse'; - -// ==============================|| NAVIGATION - GROUP ||============================== // - -export default function NavGroup({ - item, - lastItem, - remItems, - lastItemId, - setSelectedID, - setSelectedItems, - selectedItems, - setSelectedLevel, - selectedLevel -}) { - const [anchorEl, setAnchorEl] = useState(null); - const [currentItem, setCurrentItem] = useState(item); - const { pathname } = useLocation(); - - const openMini = Boolean(anchorEl); - - useEffect(() => { - if (lastItem) { - if (item.id === lastItemId) { - const localItem = { ...item }; - const elements = remItems.map((ele) => ele?.children); - localItem.children = elements.flat(1); - setCurrentItem(localItem); - } else { - setCurrentItem(item); - } - } - }, [item, lastItem, lastItemId, remItems, setCurrentItem]); - - const checkOpenForParent = useCallback( - (child, id) => { - child.forEach((ele) => { - if (ele.children?.length) { - checkOpenForParent(ele.children, currentItem.id); - } - - if (ele.url && !!matchPath({ path: ele?.link ? ele.link : ele.url, end: true }, pathname)) { - setSelectedID(id); - } - }); - }, - [currentItem.id, pathname, setSelectedID] - ); - - const checkSelectedOnload = useCallback( - (data) => { - const children = data.children ?? []; - children.forEach((itemCheck) => { - if (!itemCheck) return; - - if (itemCheck.children?.length) { - checkOpenForParent(itemCheck.children, currentItem.id); - } - - if (itemCheck.url && matchPath({ path: itemCheck.link ? itemCheck.link : itemCheck.url, end: true }, pathname)) { - setSelectedID(currentItem.id); - } - }); - }, - [pathname, currentItem, checkOpenForParent, setSelectedID] - ); - - useEffect(() => { - checkSelectedOnload(currentItem); - if (openMini) setAnchorEl(null); - }, [pathname, currentItem, checkSelectedOnload, openMini, setAnchorEl]); - - const navCollapse = item.children?.map((menuItem, index) => { - const key = menuItem.id || `${menuItem.type}-${index}`; - switch (menuItem.type) { - case 'collapse': - return ( - - ); - case 'item': - return ; - default: - return ( -
    - Fix - Group Collapse or Items -
    - ); - } - }); - - return ( - <> - -
  • - -
  • - {navCollapse} -
    - - ); -} - -NavGroup.propTypes = { - item: PropTypes.any, - lastItem: PropTypes.number, - remItems: PropTypes.array, - lastItemId: PropTypes.string, - setSelectedID: PropTypes.oneOfType([PropTypes.func, PropTypes.any]), - setSelectedItems: PropTypes.oneOfType([PropTypes.func, PropTypes.any]), - selectedItems: PropTypes.any, - setSelectedLevel: PropTypes.func, - selectedLevel: PropTypes.number, - setSelectTab: PropTypes.func -}; diff --git a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/index.jsx b/src/layout/Dashboard/Drawer/DrawerContent/Navigation/index.jsx deleted file mode 100644 index 9447a45..0000000 --- a/src/layout/Dashboard/Drawer/DrawerContent/Navigation/index.jsx +++ /dev/null @@ -1,83 +0,0 @@ -import PropTypes from 'prop-types'; -import { useState } from 'react'; - -// react-bootstrap -import ListGroup from 'react-bootstrap/ListGroup'; - -// project-imports -import NavItem from './NavItem'; -import NavGroup from './NavGroup'; -import menuItems from 'menu-items'; - -// ==============================|| NAVIGATION ||============================== // - -export default function Navigation({ selectedItems, setSelectedItems, setSelectTab }) { - const [selectedID, setSelectedID] = useState(''); - const [selectedLevel, setSelectedLevel] = useState(0); - - const lastItem = null; - let lastItemIndex = menuItems.items.length - 1; - let remItems = []; - let lastItemId; - - if (lastItem && lastItem < menuItems.items.length) { - lastItemId = menuItems.items[lastItem - 1].id; - lastItemIndex = lastItem - 1; - remItems = menuItems.items.slice(lastItem - 1, menuItems.items.length).map((item) => ({ - id: item.id, // Ensure id is included - type: item.type, // Add the missing type field - title: item.title, - elements: item.children, - icon: item.icon, - ...(item.url && { - url: item.url - }) - })); - } - - const navGroups = menuItems.items.slice(0, lastItemIndex + 1).map((item, index) => { - switch (item.type) { - case 'group': - if (item.url && item.id !== lastItemId) { - return ( - <> - - - - - ); - } - - return ( - {})} - /> - ); - default: - return ( -
    - Fix - Navigation Group -
    - ); - } - }); - - return
      {navGroups}
    ; -} - -Navigation.propTypes = { - selectedItems: PropTypes.any, - setSelectedItems: PropTypes.oneOfType([PropTypes.func, PropTypes.any]), - setSelectTab: PropTypes.oneOfType([PropTypes.func, PropTypes.any]) -}; diff --git a/src/layout/Dashboard/Drawer/DrawerContent/index.jsx b/src/layout/Dashboard/Drawer/DrawerContent/index.jsx index a07c594..6be26e3 100644 --- a/src/layout/Dashboard/Drawer/DrawerContent/index.jsx +++ b/src/layout/Dashboard/Drawer/DrawerContent/index.jsx @@ -1,153 +1,81 @@ import PropTypes from 'prop-types'; -import { useCallback, useEffect, useState } from 'react'; -import { Link, useLocation } from 'react-router-dom'; +import { useState } from 'react'; // react-bootstrap import ListGroup from 'react-bootstrap/ListGroup'; -// project-imports -import Navigation from './Navigation'; -import { useGetMenuMaster } from 'api/menu'; -import SimpleBarScroll from 'components/third-party/SimpleBar'; +// project imports +import NavItem from './NavItem'; +import NavGroup from './NavGroup'; import menuItems from 'menu-items'; -// ==============================|| DRAWER CONTENT - NAVIGATION ||============================== // - -export default function DrawerContent({ selectedItems, setSelectedItems }) { - const [selectTab, setSelectTab] = useState(menuItems.items[0]); - const { menuMaster } = useGetMenuMaster(); - const { pathname } = useLocation(); - const drawerOpen = menuMaster?.isDashboardDrawerOpened; - - const [open, setOpen] = useState({}); - - const handleClick = (item) => { - if (!item.id) return; - - const isMobile = window.innerWidth <= 1024; - - setOpen((prev) => ({ - ...prev, - [item.id]: !prev[item.id] +// ==============================|| DRAWER CONTENT ||============================== // + +export default function Navigation({ selectedItems, setSelectedItems, setSelectTab }) { + const [selectedID, setSelectedID] = useState(''); + const [selectedLevel, setSelectedLevel] = useState(0); + + const lastItem = null; + let lastItemIndex = menuItems.items.length - 1; + let remItems = []; + let lastItemId; + + if (lastItem && lastItem < menuItems.items.length) { + lastItemId = menuItems.items[lastItem - 1].id; + lastItemIndex = lastItem - 1; + remItems = menuItems.items.slice(lastItem - 1, menuItems.items.length).map((item) => ({ + id: item.id, + type: item.type, + title: item.title, + elements: item.children, + icon: item.icon, + ...(item.url && { + url: item.url + }) })); - - if (isMobile || !drawerOpen) { - setSelectedItems(item); + } + + const navGroups = menuItems.items.slice(0, lastItemIndex + 1).map((item) => { + switch (item.type) { + case 'group': + if (item.url && item.id !== lastItemId) { + return ( + + + + ); + } + + return ( + {})} + /> + ); } - }; - - const isActive = useCallback( - (item) => { - if (!item.url) return false; - return pathname.toLowerCase().includes(item.url.toLowerCase()); - }, - [pathname] - ); - - const autoOpenParents = useCallback( - (items) => { - const openMap = {}; - - const findAndMark = (entries = []) => { - entries.forEach((item) => { - if (item.children) { - const match = item.children.find((child) => isActive(child) || child.children?.some(isActive)); - if (match) openMap[item.id] = true; - - findAndMark(item.children); - } - }); - }; - - findAndMark(items); - setOpen(openMap); - }, - [isActive, setOpen] - ); - - useEffect(() => { - autoOpenParents(selectTab?.children); - }, [autoOpenParents, selectTab]); - return ( - <> - - - -
    -
    - -
      - {selectTab?.children?.map((item) => ( - - handleClick(item)}> - {item.icon && ( - - - - )} - {item.title} - {item.type === 'collapse' && ( - - - - )} - - {open[item.id] && item.children && ( -
        - {item.children.map((child) => ( -
      • - { - handleClick(child); - }} - > - {child.icon && ( - - - - )} - {child.title} - {child.type === 'collapse' && ( - - - - )} - + return ( +
        + Fix - Navigation Group +
        + ); + }); - {open[child.id] && child.children && ( -
          - {child.children.map((value) => ( -
        • - - {value.icon && ( - - - - )} - {value.title} - -
        • - ))} -
        - )} -
      • - ))} -
      - )} -
      - ))} -
    -
    -
    -
    - - ); + return
      {navGroups}
    ; } -DrawerContent.propTypes = { selectedItems: PropTypes.any, setSelectedItems: PropTypes.oneOfType([PropTypes.func, PropTypes.any]) }; +Navigation.propTypes = { + selectedItems: PropTypes.any, + setSelectedItems: PropTypes.oneOfType([PropTypes.func, PropTypes.any]), + setSelectTab: PropTypes.oneOfType([PropTypes.func, PropTypes.any]) +}; diff --git a/src/layout/Dashboard/Drawer/index.jsx b/src/layout/Dashboard/Drawer/index.jsx index f495206..021bd0a 100644 --- a/src/layout/Dashboard/Drawer/index.jsx +++ b/src/layout/Dashboard/Drawer/index.jsx @@ -1,58 +1,8 @@ -import { useEffect, useRef, useState } from 'react'; - -// react-bootstrap -import Image from 'react-bootstrap/Image'; - -// third-party - // project-imports -import DrawerContent from './DrawerContent'; -import { handlerDrawerOpen, useGetMenuMaster } from 'api/menu'; - -// assets -import logo from 'assets/images/logo-white.svg'; +import { VerticalDrawer } from '../Drawer/vertical'; // ==============================|| MAIN LAYOUT - DRAWER ||============================== // export default function MainDrawer() { - const { menuMaster } = useGetMenuMaster(); - const drawerOpen = menuMaster?.isDashboardDrawerOpened; - const [selectedItems, setSelectedItems] = useState(); - const [isMobile, setIsMobile] = useState(window.innerWidth <= 1024); - const overlayRef = useRef(null); - - useEffect(() => { - const handleResize = () => setIsMobile(window.innerWidth <= 1024); - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, []); - - useEffect(() => { - const handleClickOutside = (event) => { - if (overlayRef.current?.contains(event.target)) { - handlerDrawerOpen(false); - } - }; - if (isMobile) { - document.addEventListener('mousedown', handleClickOutside); - } - return () => document.removeEventListener('mousedown', handleClickOutside); - }, [isMobile]); - - return ( - - ); + return ; } diff --git a/src/layout/Dashboard/Drawer/vertical/VerticalDrawer.jsx b/src/layout/Dashboard/Drawer/vertical/VerticalDrawer.jsx new file mode 100644 index 0000000..227bde8 --- /dev/null +++ b/src/layout/Dashboard/Drawer/vertical/VerticalDrawer.jsx @@ -0,0 +1,57 @@ +import { useEffect, useRef, useState } from 'react'; +import { Link } from 'react-router-dom'; + +// react-bootstrap +import Image from 'react-bootstrap/Image'; + +// project-imports +import VerticalDrawerContent from './VerticalDrawerContent'; +import { handlerDrawerOpen, useGetMenuMaster } from 'api/menu'; + +// assets +import logo from 'assets/images/logo-white.svg'; + +// ==============================|| VERTICAL DRAWER ||============================== // + +export const VerticalDrawer = () => { + const { menuMaster } = useGetMenuMaster(); + const drawerOpen = menuMaster?.isDashboardDrawerOpened; + const [selectedItems, setSelectedItems] = useState(); + const [isMobile, setIsMobile] = useState(window.innerWidth <= 1024); + const overlayRef = useRef(null); + + useEffect(() => { + const handleResize = () => setIsMobile(window.innerWidth <= 1024); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + useEffect(() => { + const handleClickOutside = (event) => { + if (overlayRef.current?.contains(event.target)) { + handlerDrawerOpen(false); + } + }; + if (isMobile) { + document.addEventListener('mousedown', handleClickOutside); + } + return () => document.removeEventListener('mousedown', handleClickOutside); + }, [isMobile]); + + return ( + + ); +}; diff --git a/src/layout/Dashboard/Drawer/vertical/VerticalDrawerContent.jsx b/src/layout/Dashboard/Drawer/vertical/VerticalDrawerContent.jsx new file mode 100644 index 0000000..582ecd4 --- /dev/null +++ b/src/layout/Dashboard/Drawer/vertical/VerticalDrawerContent.jsx @@ -0,0 +1,15 @@ +import PropTypes from 'prop-types'; + +// project-imports +import SimpleBarScroll from 'components/third-party/SimpleBar'; +import Navigation from '../DrawerContent'; + +export default function VerticalDrawerContent({ selectedItems, setSelectedItems }) { + return ( + + + + ); +} + +VerticalDrawerContent.propTypes = { selectedItems: PropTypes.any, setSelectedItems: PropTypes.oneOfType([PropTypes.func, PropTypes.any]) }; diff --git a/src/layout/Dashboard/Drawer/vertical/index.js b/src/layout/Dashboard/Drawer/vertical/index.js new file mode 100644 index 0000000..88c68a8 --- /dev/null +++ b/src/layout/Dashboard/Drawer/vertical/index.js @@ -0,0 +1,2 @@ +export { VerticalDrawer } from './VerticalDrawer'; +export { default as VerticalDrawerContent } from './VerticalDrawerContent'; diff --git a/src/layout/Dashboard/Footer.jsx b/src/layout/Dashboard/Footer.jsx index 9f7b8fd..24bd407 100644 --- a/src/layout/Dashboard/Footer.jsx +++ b/src/layout/Dashboard/Footer.jsx @@ -11,7 +11,7 @@ import branding from 'branding.json'; export default function Footer() { return (