Jelajahi Sumber

rtk_tree_view_beta1

Gennadysht 1 tahun lalu
induk
melakukan
528a18b6e0

+ 122 - 53
package-lock.json

@@ -16,7 +16,8 @@
         "@fontsource/roboto": "^4.5.8",
         "@minoru/react-dnd-treeview": "^3.4.1",
         "@mui/icons-material": "^5.11.0",
-        "@mui/material": "^5.11.4",
+        "@mui/lab": "^5.0.0-alpha.118",
+        "@mui/material": "^5.11.7",
         "@mui/styled-engine-sc": "^5.11.0",
         "@reduxjs/toolkit": "^1.9.1",
         "@rtk-query/graphql-request-base-query": "^2.2.0",
@@ -3334,14 +3335,14 @@
       }
     },
     "node_modules/@mui/base": {
-      "version": "5.0.0-alpha.113",
-      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.113.tgz",
-      "integrity": "sha512-XSjvyQWATM8uk+EJZvYna8D21kOXC42lwb3q4K70btuGieKlCIQLaHTTDV2OfD4+JfT4o3NJy3I4Td2co31RZA==",
+      "version": "5.0.0-alpha.116",
+      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.116.tgz",
+      "integrity": "sha512-VwhifWdrfHc4/ZdqRZ4Gf+7P39sovNN24By1YVZdvJ9fvp0Sr8sNftGUCjYXXz+xCXVBQDXvhfxMwZrj2MvJvA==",
       "dependencies": {
         "@babel/runtime": "^7.20.7",
         "@emotion/is-prop-valid": "^1.2.0",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.2",
+        "@mui/utils": "^5.11.7",
         "@popperjs/core": "^2.11.6",
         "clsx": "^1.2.1",
         "prop-types": "^15.8.1",
@@ -3371,9 +3372,9 @@
       "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
     },
     "node_modules/@mui/core-downloads-tracker": {
-      "version": "5.11.4",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.4.tgz",
-      "integrity": "sha512-jWVwGM3vG4O0sXcW0VcIl+njCWbGCBF5vvjRpuKJajrz51AD7D6+fP1SkInZXVk5pRHf6Bnk/Yj9Of9gXxb/hA==",
+      "version": "5.11.7",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.7.tgz",
+      "integrity": "sha512-lZgX7XQTk0zVcpwEa80r+T4y09dosnUxWvFPSikU/2Hh5wnyNOek8WfJwGCNsaRiXJHMi5eHY+z8oku4u5lgNw==",
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/mui"
@@ -3404,17 +3405,63 @@
         }
       }
     },
+    "node_modules/@mui/lab": {
+      "version": "5.0.0-alpha.118",
+      "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.118.tgz",
+      "integrity": "sha512-XedMEzYT6L9JawNV70mfMhDu4+2HNXSSn4+GPtFBE1Tefl8+djwK/FXdjPaG/qZzhwMyjO/RcwXzLPR0VWLpcw==",
+      "dependencies": {
+        "@babel/runtime": "^7.20.7",
+        "@mui/base": "5.0.0-alpha.116",
+        "@mui/system": "^5.11.7",
+        "@mui/types": "^7.2.3",
+        "@mui/utils": "^5.11.7",
+        "clsx": "^1.2.1",
+        "prop-types": "^15.8.1",
+        "react-is": "^18.2.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/mui"
+      },
+      "peerDependencies": {
+        "@emotion/react": "^11.5.0",
+        "@emotion/styled": "^11.3.0",
+        "@mui/material": "^5.0.0",
+        "@types/react": "^17.0.0 || ^18.0.0",
+        "react": "^17.0.0 || ^18.0.0",
+        "react-dom": "^17.0.0 || ^18.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@emotion/react": {
+          "optional": true
+        },
+        "@emotion/styled": {
+          "optional": true
+        },
+        "@types/react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@mui/lab/node_modules/react-is": {
+      "version": "18.2.0",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+      "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+    },
     "node_modules/@mui/material": {
-      "version": "5.11.4",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.4.tgz",
-      "integrity": "sha512-ZL/czK9ynrQJ6uyDwQgK+j7m1iKA1XKPON+rEPupwAu/bJ1XJxD+H/H2bkMM8UpOkzaucx/WuMbJJGQ60l7gBg==",
+      "version": "5.11.7",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.7.tgz",
+      "integrity": "sha512-wDv7Pc6kMe9jeWkmCLt4JChd1lPc2u23JQHpB35L2VwQowpNFoDfIwqi0sYCnZTMKlRc7lza8LqwSwHl2G52Rw==",
       "dependencies": {
         "@babel/runtime": "^7.20.7",
-        "@mui/base": "5.0.0-alpha.113",
-        "@mui/core-downloads-tracker": "^5.11.4",
-        "@mui/system": "^5.11.4",
+        "@mui/base": "5.0.0-alpha.116",
+        "@mui/core-downloads-tracker": "^5.11.7",
+        "@mui/system": "^5.11.7",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.2",
+        "@mui/utils": "^5.11.7",
         "@types/react-transition-group": "^4.4.5",
         "clsx": "^1.2.1",
         "csstype": "^3.1.1",
@@ -3454,12 +3501,12 @@
       "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
     },
     "node_modules/@mui/private-theming": {
-      "version": "5.11.2",
-      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.2.tgz",
-      "integrity": "sha512-qZwMaqRFPwlYmqwVKblKBGKtIjJRAj3nsvX93pOmatsXyorW7N/0IPE/swPgz1VwChXhHO75DwBEx8tB+aRMNg==",
+      "version": "5.11.7",
+      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.7.tgz",
+      "integrity": "sha512-XzRTSZdc8bhuUdjablTNv3kFkZ/XIMlKkOqqJCU0G8W3tWGXpau2DXkafPd1ddjPhF9zF3qLKNGgKCChYItjgA==",
       "dependencies": {
         "@babel/runtime": "^7.20.7",
-        "@mui/utils": "^5.11.2",
+        "@mui/utils": "^5.11.7",
         "prop-types": "^15.8.1"
       },
       "engines": {
@@ -3536,15 +3583,15 @@
       }
     },
     "node_modules/@mui/system": {
-      "version": "5.11.4",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.4.tgz",
-      "integrity": "sha512-fE2Ts33V5zh7ouciwXgMm/a6sLvjIj9OMeojuHNYY7BStTxparC/Fp9CNUZNJwt76U6ZJC59aYScFSRQKbW08g==",
+      "version": "5.11.7",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.7.tgz",
+      "integrity": "sha512-uGB6hBxGlAdlmbLdTtUZYNPXkgQGGnKxHdkRATqsu7UlCxNsc/yS5NCEWy/3c4pnelD1LDLD39WrntP9mwhfkQ==",
       "dependencies": {
         "@babel/runtime": "^7.20.7",
-        "@mui/private-theming": "^5.11.2",
+        "@mui/private-theming": "^5.11.7",
         "@mui/styled-engine": "^5.11.0",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.2",
+        "@mui/utils": "^5.11.7",
         "clsx": "^1.2.1",
         "csstype": "^3.1.1",
         "prop-types": "^15.8.1"
@@ -3588,9 +3635,9 @@
       }
     },
     "node_modules/@mui/utils": {
-      "version": "5.11.2",
-      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.2.tgz",
-      "integrity": "sha512-AyizuHHlGdAtH5hOOXBW3kriuIwUIKUIgg0P7LzMvzf6jPhoQbENYqY6zJqfoZ7fAWMNNYT8mgN5EftNGzwE2w==",
+      "version": "5.11.7",
+      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.7.tgz",
+      "integrity": "sha512-8uyNDeVHZA804Ego20Erv8TpxlbqTe/EbhTI2H1UYr4/RiIbBprat8W4Qqr2UQIsC/b3DLz+0RQ6R/E5BxEcLA==",
       "dependencies": {
         "@babel/runtime": "^7.20.7",
         "@types/prop-types": "^15.7.5",
@@ -23390,14 +23437,14 @@
       }
     },
     "@mui/base": {
-      "version": "5.0.0-alpha.113",
-      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.113.tgz",
-      "integrity": "sha512-XSjvyQWATM8uk+EJZvYna8D21kOXC42lwb3q4K70btuGieKlCIQLaHTTDV2OfD4+JfT4o3NJy3I4Td2co31RZA==",
+      "version": "5.0.0-alpha.116",
+      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.116.tgz",
+      "integrity": "sha512-VwhifWdrfHc4/ZdqRZ4Gf+7P39sovNN24By1YVZdvJ9fvp0Sr8sNftGUCjYXXz+xCXVBQDXvhfxMwZrj2MvJvA==",
       "requires": {
         "@babel/runtime": "^7.20.7",
         "@emotion/is-prop-valid": "^1.2.0",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.2",
+        "@mui/utils": "^5.11.7",
         "@popperjs/core": "^2.11.6",
         "clsx": "^1.2.1",
         "prop-types": "^15.8.1",
@@ -23412,9 +23459,9 @@
       }
     },
     "@mui/core-downloads-tracker": {
-      "version": "5.11.4",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.4.tgz",
-      "integrity": "sha512-jWVwGM3vG4O0sXcW0VcIl+njCWbGCBF5vvjRpuKJajrz51AD7D6+fP1SkInZXVk5pRHf6Bnk/Yj9Of9gXxb/hA=="
+      "version": "5.11.7",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.7.tgz",
+      "integrity": "sha512-lZgX7XQTk0zVcpwEa80r+T4y09dosnUxWvFPSikU/2Hh5wnyNOek8WfJwGCNsaRiXJHMi5eHY+z8oku4u5lgNw=="
     },
     "@mui/icons-material": {
       "version": "5.11.0",
@@ -23424,17 +23471,39 @@
         "@babel/runtime": "^7.20.6"
       }
     },
+    "@mui/lab": {
+      "version": "5.0.0-alpha.118",
+      "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.118.tgz",
+      "integrity": "sha512-XedMEzYT6L9JawNV70mfMhDu4+2HNXSSn4+GPtFBE1Tefl8+djwK/FXdjPaG/qZzhwMyjO/RcwXzLPR0VWLpcw==",
+      "requires": {
+        "@babel/runtime": "^7.20.7",
+        "@mui/base": "5.0.0-alpha.116",
+        "@mui/system": "^5.11.7",
+        "@mui/types": "^7.2.3",
+        "@mui/utils": "^5.11.7",
+        "clsx": "^1.2.1",
+        "prop-types": "^15.8.1",
+        "react-is": "^18.2.0"
+      },
+      "dependencies": {
+        "react-is": {
+          "version": "18.2.0",
+          "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+          "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
+        }
+      }
+    },
     "@mui/material": {
-      "version": "5.11.4",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.4.tgz",
-      "integrity": "sha512-ZL/czK9ynrQJ6uyDwQgK+j7m1iKA1XKPON+rEPupwAu/bJ1XJxD+H/H2bkMM8UpOkzaucx/WuMbJJGQ60l7gBg==",
+      "version": "5.11.7",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.7.tgz",
+      "integrity": "sha512-wDv7Pc6kMe9jeWkmCLt4JChd1lPc2u23JQHpB35L2VwQowpNFoDfIwqi0sYCnZTMKlRc7lza8LqwSwHl2G52Rw==",
       "requires": {
         "@babel/runtime": "^7.20.7",
-        "@mui/base": "5.0.0-alpha.113",
-        "@mui/core-downloads-tracker": "^5.11.4",
-        "@mui/system": "^5.11.4",
+        "@mui/base": "5.0.0-alpha.116",
+        "@mui/core-downloads-tracker": "^5.11.7",
+        "@mui/system": "^5.11.7",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.2",
+        "@mui/utils": "^5.11.7",
         "@types/react-transition-group": "^4.4.5",
         "clsx": "^1.2.1",
         "csstype": "^3.1.1",
@@ -23451,12 +23520,12 @@
       }
     },
     "@mui/private-theming": {
-      "version": "5.11.2",
-      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.2.tgz",
-      "integrity": "sha512-qZwMaqRFPwlYmqwVKblKBGKtIjJRAj3nsvX93pOmatsXyorW7N/0IPE/swPgz1VwChXhHO75DwBEx8tB+aRMNg==",
+      "version": "5.11.7",
+      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.7.tgz",
+      "integrity": "sha512-XzRTSZdc8bhuUdjablTNv3kFkZ/XIMlKkOqqJCU0G8W3tWGXpau2DXkafPd1ddjPhF9zF3qLKNGgKCChYItjgA==",
       "requires": {
         "@babel/runtime": "^7.20.7",
-        "@mui/utils": "^5.11.2",
+        "@mui/utils": "^5.11.7",
         "prop-types": "^15.8.1"
       }
     },
@@ -23481,15 +23550,15 @@
       }
     },
     "@mui/system": {
-      "version": "5.11.4",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.4.tgz",
-      "integrity": "sha512-fE2Ts33V5zh7ouciwXgMm/a6sLvjIj9OMeojuHNYY7BStTxparC/Fp9CNUZNJwt76U6ZJC59aYScFSRQKbW08g==",
+      "version": "5.11.7",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.7.tgz",
+      "integrity": "sha512-uGB6hBxGlAdlmbLdTtUZYNPXkgQGGnKxHdkRATqsu7UlCxNsc/yS5NCEWy/3c4pnelD1LDLD39WrntP9mwhfkQ==",
       "requires": {
         "@babel/runtime": "^7.20.7",
-        "@mui/private-theming": "^5.11.2",
+        "@mui/private-theming": "^5.11.7",
         "@mui/styled-engine": "^5.11.0",
         "@mui/types": "^7.2.3",
-        "@mui/utils": "^5.11.2",
+        "@mui/utils": "^5.11.7",
         "clsx": "^1.2.1",
         "csstype": "^3.1.1",
         "prop-types": "^15.8.1"
@@ -23502,9 +23571,9 @@
       "requires": {}
     },
     "@mui/utils": {
-      "version": "5.11.2",
-      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.2.tgz",
-      "integrity": "sha512-AyizuHHlGdAtH5hOOXBW3kriuIwUIKUIgg0P7LzMvzf6jPhoQbENYqY6zJqfoZ7fAWMNNYT8mgN5EftNGzwE2w==",
+      "version": "5.11.7",
+      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.7.tgz",
+      "integrity": "sha512-8uyNDeVHZA804Ego20Erv8TpxlbqTe/EbhTI2H1UYr4/RiIbBprat8W4Qqr2UQIsC/b3DLz+0RQ6R/E5BxEcLA==",
       "requires": {
         "@babel/runtime": "^7.20.7",
         "@types/prop-types": "^15.7.5",

+ 2 - 1
package.json

@@ -11,7 +11,8 @@
     "@fontsource/roboto": "^4.5.8",
     "@minoru/react-dnd-treeview": "^3.4.1",
     "@mui/icons-material": "^5.11.0",
-    "@mui/material": "^5.11.4",
+    "@mui/lab": "^5.0.0-alpha.118",
+    "@mui/material": "^5.11.7",
     "@mui/styled-engine-sc": "^5.11.0",
     "@reduxjs/toolkit": "^1.9.1",
     "@rtk-query/graphql-request-base-query": "^2.2.0",

+ 3 - 2
src/App.js

@@ -1,7 +1,7 @@
 import { Router, Route, Switch } from 'react-router-dom';
 import { store, persistedStore } from './store';
 import { Provider } from 'react-redux';
-import { CCategoriesList, CEditableCategory, CEditableGood, CGood, CGoodsList, CLoginForm, CMainAppBar, COrder, COrdersList, CRegisterForm, CUser, CUsersList } from "./Components";
+import { CCategoriesList, CCategoryTree, CEditableCategory, CEditableGood, CGood, CGoodsList, CLoginForm, CMainAppBar, COrder, COrdersList, CRegisterForm, CUser, CUsersList } from "./Components";
 import { CLogout } from './Components';
 import { CSidebar } from './Components/Sidebar';
 import { CRootCats } from './Components';
@@ -61,7 +61,8 @@ function App() {
                 <Route path="/user/:_id" component={CUser} />
                 <Route path="/user" component={CUser} />
                 <Route path="/logout" component={CLogout} />
-                <Route path="/catree" component={CCategoriesList} />
+                <Route path="/catree" component={CCategoryTree} />
+                <Route path="/categories" component={CCategoriesList} />
                 <Route path="*" component={NotFound} />
               </Switch>
             </div>

+ 10 - 3
src/Components/Category.js

@@ -44,7 +44,7 @@ const Category = () => {
                             </MyLink>
                         )
                     }
-                    <Typography paragraph gutterBottom component={'h3'} variant={'h3'} sx={{marginTop: "1vh"} } >
+                    <Typography paragraph gutterBottom component={'h3'} variant={'h3'} sx={{ marginTop: "1vh" }} >
                         {cat.name}
                     </Typography>
                     {csubCats && <CSubCategories />}
@@ -53,8 +53,15 @@ const Category = () => {
                             {cat.subCategories.map(scat => (
                                 <ListItem key={scat._id} disablePadding>
                                     <ListItemButton>
-                                        <MyLink to={`/category/${scat._id}`}>
-                                            <ListItemText primary={scat.name} />
+                                        <MyLink to={`/category/${scat._id}`} >
+                                            <ListItemText
+                                                disableTypography
+                                                primary={
+                                                    <Typography paragraph gutterBottom component={'h4'} variant={'h4'} sx={{ marginTop: "1vh", marginLeft: "18vh"}} >
+                                                        {scat.name}
+                                                    </Typography>
+                                                } 
+                                            />
                                         </MyLink>
                                     </ListItemButton>
                                 </ListItem>

+ 105 - 75
src/Components/CategoryTree.js

@@ -1,108 +1,120 @@
 import React, { useState } from "react";
-import { Typography } from "@mui/material"
+import { Button, Typography } from "@mui/material"
 import { DndProvider } from "react-dnd";
 import {
     Tree,
     MultiBackend,
     getBackendOptions
 } from "@minoru/react-dnd-treeview";
-import { useGetRootCategoriesQuery } from "../reducers";
+import { useGetRootCategoriesQuery, useSaveCategoryMutation } from "../reducers";
+import { CategoryTreeItem } from "./CategoryTreeItem";
+import { ThemeProvider, CssBaseline } from "@mui/material";
+import { createTheme } from "@mui/material/styles";
+import styles from "./CategoryTree.module.css";
 
-const SampleData = [
-    {
-        "id": 1,
-        "parent": 0,
-        "droppable": true,
-        "text": "Folder 1"
-    },
-    {
-        "id": 2,
-        "parent": 1,
-        "droppable": false,
-        "text": "File 1-1"
-    },
-    {
-        "id": 3,
-        "parent": 1,
-        "droppable": false,
-        "text": "File 1-2"
-    },
-    {
-        "id": 4,
-        "parent": 0,
-        "droppable": true,
-        "text": "Folder 2"
-    },
-    {
-        "id": 5,
-        "parent": 4,
-        "droppable": true,
-        "text": "Folder 2-1"
-    },
-    {
-        "id": 6,
-        "parent": 5,
-        "droppable": false,
-        "text": "File 2-1-1"
-    },
-    {
-        "id": 7,
-        "parent": 0,
-        "droppable": false,
-        "text": "File 3"
+export const theme = createTheme({
+    components: {
+        MuiCssBaseline: {
+            styleOverrides: {
+                "*": {
+                    margin: 0,
+                    padding: 0
+                },
+                "html, body, #root": {
+                    height: "100%"
+                },
+                ul: {
+                    listStyle: "none"
+                }
+            }
+        },
+        MuiSvgIcon: {
+            styleOverrides: {
+                root: { verticalAlign: "middle" }
+            }
+        }
     }
-];
-
+});
 
 
 const logTree = (treeData) => {
     console.log(treeData.map(el => { return { id: el.id, parent: el.parent, text: el.text } }));
 }
 
-const CategoryTree = ({ elements }) => {
+const CategoryTree = ({ elements, saveCategory }) => {
     console.log(elements);
     const [treeData, setTreeData] = useState(elements);
-    const handleDrop = (newTree) => {
-        let a = '';
-        setTreeData(newTree);
-        logTree(newTree)
-    }
 
+    const handleDrop = (newTree, params) => {
+        let targetCat = params.dropTarget?.cat;
+        let sourceCat = params.dragSource?.cat;
+        if (!sourceCat)
+            throw new Error("No source");
+
+        if (sourceCat.parent?._id != targetCat?._id) {
+            sourceCat.parent = targetCat ? { _id: targetCat._id, name: targetCat.name } : null;
+            saveCategory(sourceCat);
+            setTreeData(newTree);
+            //logTree(newTree)
+        }
+    }
+    console.log(styles);
     return (
-        <div>
+        <ThemeProvider theme={theme}>
+            <CssBaseline />
             <DndProvider backend={MultiBackend} options={getBackendOptions()}>
-                <Tree
-                    tree={treeData}
-                    rootId={0}
-                    render={(node, { depth, isOpen, onToggle }) => (
-                        <div style={{ marginInlineStart: depth * 10 }}>
-                            {node.droppable && (
-                                <span onClick={onToggle}>{isOpen ? "[-]" : "[+]"}</span>
-                            )}
-                            {node.text}
-                        </div>
-                    )}
-                    dragPreviewRender={(monitorProps) => (
-                        <div>{monitorProps.item.text}</div>
-                    )}
-                    onDrop={handleDrop}
-                />
+                <div className={styles.app}>
+                    <Tree
+                        style={{ listStyleType: 'none', paddingLeft: '0px', height: 240, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }}
+                        tree={treeData}
+                        rootId={0}
+                        render={(node, { depth, isOpen, onToggle }) => (
+                            <CategoryTreeItem
+                                node={node}
+                                depth={depth}
+                                isOpen={isOpen}
+                                onToggle={onToggle}
+                                saveCategoryName={(node, name) => {
+                                    if (!node?.cat || node.cat.name === name)
+                                        return;
+                                    node.text = node.cat.name = name;
+                                    saveCategory(node.cat);
+                                }}
+                            />
+                        )}
+                        dragPreviewRender={(monitorProps) => (
+                            <div>{monitorProps.item.text}</div>
+                        )}
+                        onDrop={handleDrop}
+                        classes={{
+                            root: styles.treeRoot,
+                            draggingSource: styles.draggingSource,
+                            dropTarget: styles.dropTarget
+                        }}
+                    />
+                </div>
             </DndProvider>
-        </div>
+        </ThemeProvider>
     );
 }
 
-let index = 1;
-function wrapToTreeItems(cats, parentCat = undefined, catTreeItems = undefined) {
+/*
+                        /*rootProps={{ style: { listStyleType: 'none' } }}
+                        listComponent="ul"
+                        listItemComponent="li"
+*/
+
+let index = 2;
+function wrapToTreeItems(cats, parentCat = null, catTreeItems = undefined) {
     catTreeItems ??= [];
     if (cats) {
         for (let cat of cats) {
             let catTreeItem = {
                 "id": index++,
-                "parent": parentCat?.id ?? 0,
+                "parent": parentCat?.id ?? 1,
                 "droppable": true,
                 "text": cat.name,
-                "cat": cat
+                "cat": { _id: cat._id, name: cat.name, parent: parentCat?.cat ?? null }
             };
             catTreeItems.push(catTreeItem);
             wrapToTreeItems(cat.subCategories, catTreeItem, catTreeItems)
@@ -114,7 +126,25 @@ function wrapToTreeItems(cats, parentCat = undefined, catTreeItems = undefined)
 const CCategoryTree = () => {
     const { isLoading, data } = useGetRootCategoriesQuery(true);
     let cats = data?.CategoryFind;
-    return !isLoading && cats && <CategoryTree elements={wrapToTreeItems(cats)} />
+
+    let catTreeItems = [];
+    let rootCat = {
+        id: 1,
+        parent: 0,
+        droppable: true,
+        text: "...",
+        cat: null
+    }
+    catTreeItems.push(rootCat);
+
+    const [saveCategoryMutation, { }] = useSaveCategoryMutation(true);
+
+    const saveCategory = async (category) => {
+        let res = await saveCategoryMutation({ category });
+        return true;
+    }
+
+    return !isLoading && cats && <CategoryTree elements={wrapToTreeItems(cats, rootCat, catTreeItems)} saveCategory={saveCategory} />
 }
 
 export { CCategoryTree };

+ 15 - 0
src/Components/CategoryTree.module.css

@@ -0,0 +1,15 @@
+.app {
+    height: 100%;
+}
+
+.treeRoot {
+    height: 100%;
+}
+
+.draggingSource {
+    opacity: .3;
+}
+
+.dropTarget {
+    background-color: #e8f0fe;
+}

+ 83 - 0
src/Components/CategoryTreeItem.js

@@ -0,0 +1,83 @@
+import React from "react";
+import { Typography, Button, Input, TextField } from "@mui/material";
+import ArrowRightIcon from "@mui/icons-material/ArrowRight";
+import styles from "./CategoryTreeItem.module.css";
+import ListAltIcon from "@mui/icons-material/ListAlt";
+
+export const CategoryTreeItem = (props) => {
+    const { droppable, data } = props.node;
+    const indent = props.depth * 40;
+
+    const classes = {
+        customInput: {
+            fontFamily: '"Comic Sans MS", cursive, sans-serif',
+            fontSize: "0.05em",
+            fontWeight: "bold"
+        },
+    };
+
+
+    const handleToggle = (e) => {
+        e.stopPropagation();
+        props.onToggle(props.node.id);
+    };
+
+    /*
+                            <Button sx={{ marginInlineStart: depth * 10 }} label={node.text}>
+                                {node.droppable && (
+                                    <span onClick={onToggle}>{isOpen ? "[-]" : "[+]"}</span>
+                                )}
+                                {node.text}
+                            </Button>
+    */
+    return (
+        <div
+            className={`tree-node ${styles.root}`}
+            style={{ paddingInlineStart: indent }}
+        >
+            <div
+                className={`${styles.expandIconWrapper} ${props.isOpen ? styles.isOpen : ""
+                    }`}
+            >
+                {props.node.droppable && (
+                    <div onClick={handleToggle}>
+                        <ArrowRightIcon color="secondary" />
+                    </div>
+                )}
+            </div>
+            <div>
+                <ListAltIcon color="secondary" droppable={droppable} fileType={data?.fileType} />
+            </div>
+            <div className={styles.labelGridItem}>
+                <TextField
+                    color="primary"
+                    InputProps={{
+                        sx: {
+                            "& input": {
+                                fontSize: "1.5em",
+                                display: 'flex',
+                                alignItems: 'center',
+                                color: 'darkBlue',
+                                fontWeight: 'bold',
+                            }
+                        },
+                    }}
+                    id="filled-basic" defaultValue={props.node.text} variant="filled" size="small"
+                    onBlur={e => props.saveCategoryName(props.node, e.target.value)}
+                    onKeyUp={(e) => {
+                        if (e.key === 'Escape') {
+                            e.target.value = e.target.defaultValue;
+                            console.log('Enter key pressed');
+                            // write your functionality here
+                        }
+                        else if (e.key === 'Enter') {
+                            e.target.focused = false;
+                        }
+                    }}
+                />
+            </div>
+        </div>
+    );
+    //                <Typography sx={{ fontSize: "larger", fontWeight: "bold" }} size="large" color="primary">{props.node.text}</Typography>
+
+};

+ 30 - 0
src/Components/CategoryTreeItem.module.css

@@ -0,0 +1,30 @@
+.root {
+    align-items: center;
+    display: grid;
+    grid-template-columns: auto auto 1fr auto;
+    height: 62px;
+    padding-inline-end: 8px;
+  }
+  
+  .expandIconWrapper {
+    align-items: center;
+    font-size: 0;
+    cursor: pointer;
+    display: flex;
+    height: 50px;
+    justify-content: center;
+    width: 50px;
+    transition: transform linear .1s;
+    transform: rotate(0deg);
+  }
+  
+  .expandIconWrapper.isOpen {
+    transform: rotate(90deg);
+  }
+  
+  .labelGridItem {
+    padding-inline-start: 25px;
+    text-align:left;
+  }
+
+

+ 3 - 2
src/Components/MainAppBar.js

@@ -19,7 +19,7 @@ import SupervisedUserCircleIcon from '@mui/icons-material/SupervisedUserCircle';
 import WorkHistoryIcon from '@mui/icons-material/WorkHistory';
 import { Badge, Paper, Tooltip } from '@mui/material';
 import logo from '../images/logo.jpg';
-
+import AccountTreeIcon from '@mui/icons-material/AccountTree';
 const MainAppBar = ({ token, openSidebar }) => {
     const theme = useTheme();
     const cartItemsCount = useSelector(state => getCartItemsCount(state) ?? 0);
@@ -57,7 +57,8 @@ const MainAppBar = ({ token, openSidebar }) => {
                         <>
                             {isAdmin && (
                                 <>
-                                    <MyLink to="/catree"><Button sx={{ color: "white" }}><Tooltip title="Categories"><CategoryIcon /></Tooltip></Button></MyLink>
+                                    <MyLink to="/categories"><Button sx={{ color: "white" }}><Tooltip title="Categories"><CategoryIcon /></Tooltip></Button></MyLink>
+                                    <MyLink to="/catree"><Button sx={{ color: "white" }}><Tooltip title="Categories Tree"><AccountTreeIcon/></Tooltip></Button></MyLink>
                                     <MyLink to="/users"><Button sx={{ color: "white" }}><Tooltip title="Users"><SupervisedUserCircleIcon /></Tooltip></Button></MyLink>
                                 </>
                             )}

+ 6 - 0
src/reducers/categoryReducer.js

@@ -110,6 +110,12 @@ export const categoryApi = createApi({
                     variables: { category: { ...category } }
                 }
             ),
+            transformResponse: (response, meta, arg) => {
+                return response;
+            },
+            transformErrorResponse: (response, meta, arg) => {
+                return response;
+            },
             invalidatesTags: (result, error, arg) => {
                 if (!error) {
                     let catInv = { type: 'Category', _id: arg.category._id };

TEMPAT SAMPAH
src/theme_main.png