From 53346740ab8e1a44b4d80bea59ba7f5d25776011 Mon Sep 17 00:00:00 2001 From: Sina Blattmann Date: Thu, 9 Mar 2023 10:50:56 +0100 Subject: [PATCH] [WIP] create tree view with drag and drop functionality --- typescript/Frontend/package-lock.json | 575 +++++++++++++++++- typescript/Frontend/package.json | 7 + .../components/Groups/DragPreview.module.scss | 20 + .../src/components/Groups/DragPreview.tsx | 23 + .../Groups/{Group.tsx => Folder.tsx} | 13 +- .../src/components/Groups/FolderForm.tsx | 89 +++ .../src/components/Groups/GroupTabs.tsx | 39 +- .../components/Groups/GroupTree.module.scss | 20 + .../src/components/Groups/GroupTree.tsx | 138 +++-- .../Frontend/src/components/Groups/Groups.tsx | 9 +- .../components/Groups/TreeNode.module.scss | 27 + .../src/components/Groups/TreeNode.tsx | 62 ++ .../src/components/Groups/TypeIcon.tsx | 15 + typescript/Frontend/src/lang/de.json | 3 +- typescript/Frontend/src/lang/en.json | 4 +- typescript/Frontend/src/routes.json | 3 +- 16 files changed, 971 insertions(+), 76 deletions(-) create mode 100644 typescript/Frontend/src/components/Groups/DragPreview.module.scss create mode 100644 typescript/Frontend/src/components/Groups/DragPreview.tsx rename typescript/Frontend/src/components/Groups/{Group.tsx => Folder.tsx} (84%) create mode 100644 typescript/Frontend/src/components/Groups/FolderForm.tsx create mode 100644 typescript/Frontend/src/components/Groups/GroupTree.module.scss create mode 100644 typescript/Frontend/src/components/Groups/TreeNode.module.scss create mode 100644 typescript/Frontend/src/components/Groups/TreeNode.tsx create mode 100644 typescript/Frontend/src/components/Groups/TypeIcon.tsx diff --git a/typescript/Frontend/package-lock.json b/typescript/Frontend/package-lock.json index b3b2c1d8a..36f44c775 100644 --- a/typescript/Frontend/package-lock.json +++ b/typescript/Frontend/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", + "@minoru/react-dnd-treeview": "^3.4.1", "@mui/icons-material": "^5.11.0", "@mui/lab": "^5.0.0-alpha.120", "@mui/material": "^5.11.7", @@ -23,14 +24,20 @@ "@types/react-router-dom": "^5.3.3", "axios": "^1.3.1", "chart.js": "^4.2.1", + "css-loader": "^6.7.3", "formik": "^2.2.9", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", "react-intl": "^6.2.10", "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", "reactflow": "^11.5.6", + "sass": "^1.58.3", + "sass-loader": "^13.2.0", + "style-loader": "^3.3.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, @@ -3181,6 +3188,11 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" + }, "node_modules/@kurkle/color": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", @@ -3191,6 +3203,82 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@minoru/react-dnd-treeview": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@minoru/react-dnd-treeview/-/react-dnd-treeview-3.4.1.tgz", + "integrity": "sha512-FnxWRY1VM++glt43hqtwSuGmE6K1vWvNWsf69KoADvXHnUAu85+EIdYD1ONgAdLdOzXL5rotl7E0FLeGidaDyg==", + "dependencies": { + "@juggle/resize-observer": "^3.3.1", + "dnd-multi-backend": "^7.0.0-alpha.4", + "framer-motion": "^6.2.8", + "react-dnd-html5-backend": "^16.0.1", + "react-dnd-touch-backend": "^16.0.1", + "react-use-measure": "^2.1.1" + }, + "peerDependencies": { + "react": "17.x || 18.x", + "react-dnd": "15.x || 16.x", + "react-dom": "17.x || 18.x" + } + }, + "node_modules/@motionone/animation": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.15.1.tgz", + "integrity": "sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==", + "dependencies": { + "@motionone/easing": "^10.15.1", + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/dom": { + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz", + "integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==", + "dependencies": { + "@motionone/animation": "^10.12.0", + "@motionone/generators": "^10.12.0", + "@motionone/types": "^10.12.0", + "@motionone/utils": "^10.12.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/easing": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.15.1.tgz", + "integrity": "sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw==", + "dependencies": { + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/generators": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.15.1.tgz", + "integrity": "sha512-67HLsvHJbw6cIbLA/o+gsm7h+6D4Sn7AUrB/GPxvujse1cGZ38F5H7DzoH7PhX+sjvtDnt2IhFYF2Zp1QTMKWQ==", + "dependencies": { + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "node_modules/@motionone/types": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.15.1.tgz", + "integrity": "sha512-iIUd/EgUsRZGrvW0jqdst8st7zKTzS9EsKkP+6c6n4MPZoQHwiHuVtTQLD6Kp0bsBLhNzKIBlHXponn/SDT4hA==" + }, + "node_modules/@motionone/utils": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.15.1.tgz", + "integrity": "sha512-p0YncgU+iklvYr/Dq4NobTRdAPv9PveRDUXabPEeOjBLSO/1FNB2phNTZxOxpi1/GZwYpAoECEa0Wam+nsmhSw==", + "dependencies": { + "@motionone/types": "^10.15.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, "node_modules/@mui/base": { "version": "5.0.0-alpha.117", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.117.tgz", @@ -3631,6 +3719,21 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "node_modules/@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "node_modules/@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, "node_modules/@reactflow/background": { "version": "11.1.8", "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.1.8.tgz", @@ -7250,6 +7353,11 @@ "node": ">=10" } }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -7470,6 +7578,21 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "node_modules/dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dependencies": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, + "node_modules/dnd-multi-backend": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/dnd-multi-backend/-/dnd-multi-backend-7.1.3.tgz", + "integrity": "sha512-INOAt4p/5fkaAUpXu0I+ialm1Ewi9HmIhs/558RFrhBZ0s/XGL991w3A2GvBuaPQNsZJEzCJh0mv/0dswxfSfQ==" + }, "node_modules/dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -9203,6 +9326,49 @@ "url": "https://www.patreon.com/infusion" } }, + "node_modules/framer-motion": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", + "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", + "dependencies": { + "@motionone/dom": "10.12.0", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": ">=16.8 || ^17.0.0 || ^18.0.0", + "react-dom": ">=16.8 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/framer-motion/node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/framer-motion/node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, + "node_modules/framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -9583,6 +9749,11 @@ "he": "bin/he" } }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -9877,6 +10048,11 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz", + "integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -13677,6 +13853,17 @@ "node": ">=4" } }, + "node_modules/popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "dependencies": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, "node_modules/postcss": { "version": "8.4.21", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", @@ -15266,6 +15453,52 @@ "node": ">=8" } }, + "node_modules/react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "dependencies": { + "dnd-core": "^16.0.1" + } + }, + "node_modules/react-dnd-touch-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-touch-backend/-/react-dnd-touch-backend-16.0.1.tgz", + "integrity": "sha512-NonoCABzzjyWGZuDxSG77dbgMZ2Wad7eQiCd/ECtsR2/NBLTjGksPUx9UPezZ1nQ/L7iD130Tz3RUshL/ClKLA==", + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "dnd-core": "^16.0.1" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -15440,6 +15673,43 @@ "node": ">=10" } }, + "node_modules/react-scripts/node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, "node_modules/react-scripts/node_modules/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -15474,6 +15744,18 @@ "react-dom": ">=16.6.0" } }, + "node_modules/react-use-measure": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz", + "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==", + "dependencies": { + "debounce": "^1.2.1" + }, + "peerDependencies": { + "react": ">=16.13", + "react-dom": ">=16.13" + } + }, "node_modules/reactflow": { "version": "11.5.6", "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.5.6.tgz", @@ -15545,6 +15827,14 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -15953,16 +16243,32 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "node_modules/sass": { + "version": "1.58.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.3.tgz", + "integrity": "sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", + "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==", "dependencies": { "klona": "^2.0.4", "neo-async": "^2.6.2" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", @@ -15970,7 +16276,7 @@ }, "peerDependencies": { "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "sass": "^1.3.0", "sass-embedded": "*", "webpack": "^5.0.0" @@ -16579,6 +16885,15 @@ "webpack": "^5.0.0" } }, + "node_modules/style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -20458,6 +20773,11 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" + }, "@kurkle/color": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", @@ -20468,6 +20788,77 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "@minoru/react-dnd-treeview": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@minoru/react-dnd-treeview/-/react-dnd-treeview-3.4.1.tgz", + "integrity": "sha512-FnxWRY1VM++glt43hqtwSuGmE6K1vWvNWsf69KoADvXHnUAu85+EIdYD1ONgAdLdOzXL5rotl7E0FLeGidaDyg==", + "requires": { + "@juggle/resize-observer": "^3.3.1", + "dnd-multi-backend": "^7.0.0-alpha.4", + "framer-motion": "^6.2.8", + "react-dnd-html5-backend": "^16.0.1", + "react-dnd-touch-backend": "^16.0.1", + "react-use-measure": "^2.1.1" + } + }, + "@motionone/animation": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.15.1.tgz", + "integrity": "sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==", + "requires": { + "@motionone/easing": "^10.15.1", + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "@motionone/dom": { + "version": "10.12.0", + "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz", + "integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==", + "requires": { + "@motionone/animation": "^10.12.0", + "@motionone/generators": "^10.12.0", + "@motionone/types": "^10.12.0", + "@motionone/utils": "^10.12.0", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, + "@motionone/easing": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.15.1.tgz", + "integrity": "sha512-6hIHBSV+ZVehf9dcKZLT7p5PEKHGhDwky2k8RKkmOvUoYP3S+dXsKupyZpqx5apjd9f+php4vXk4LuS+ADsrWw==", + "requires": { + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "@motionone/generators": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.15.1.tgz", + "integrity": "sha512-67HLsvHJbw6cIbLA/o+gsm7h+6D4Sn7AUrB/GPxvujse1cGZ38F5H7DzoH7PhX+sjvtDnt2IhFYF2Zp1QTMKWQ==", + "requires": { + "@motionone/types": "^10.15.1", + "@motionone/utils": "^10.15.1", + "tslib": "^2.3.1" + } + }, + "@motionone/types": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.15.1.tgz", + "integrity": "sha512-iIUd/EgUsRZGrvW0jqdst8st7zKTzS9EsKkP+6c6n4MPZoQHwiHuVtTQLD6Kp0bsBLhNzKIBlHXponn/SDT4hA==" + }, + "@motionone/utils": { + "version": "10.15.1", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.15.1.tgz", + "integrity": "sha512-p0YncgU+iklvYr/Dq4NobTRdAPv9PveRDUXabPEeOjBLSO/1FNB2phNTZxOxpi1/GZwYpAoECEa0Wam+nsmhSw==", + "requires": { + "@motionone/types": "^10.15.1", + "hey-listen": "^1.0.8", + "tslib": "^2.3.1" + } + }, "@mui/base": { "version": "5.0.0-alpha.117", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.117.tgz", @@ -20676,6 +21067,21 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" }, + "@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, "@reactflow/background": { "version": "11.1.8", "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.1.8.tgz", @@ -23423,6 +23829,11 @@ "whatwg-url": "^8.0.0" } }, + "debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -23584,6 +23995,21 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "requires": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, + "dnd-multi-backend": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/dnd-multi-backend/-/dnd-multi-backend-7.1.3.tgz", + "integrity": "sha512-INOAt4p/5fkaAUpXu0I+ialm1Ewi9HmIhs/558RFrhBZ0s/XGL991w3A2GvBuaPQNsZJEzCJh0mv/0dswxfSfQ==" + }, "dns-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", @@ -24862,6 +25288,45 @@ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" }, + "framer-motion": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", + "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", + "requires": { + "@emotion/is-prop-valid": "^0.8.2", + "@motionone/dom": "10.12.0", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "requires": { + "@emotion/memoize": "0.7.4" + } + }, + "@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + } + } + }, + "framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "requires": { + "tslib": "^2.1.0" + } + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -25123,6 +25588,11 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, + "hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, "hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -25348,6 +25818,11 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.19.tgz", "integrity": "sha512-eY+Y0qcsB4TZKwgQzLaE/lqYMlKhv5J9dyd2RhhtGhNo2njPXDqU9XPfcNfa3MIDsdtZt5KlkIsirlo4dHsWdQ==" }, + "immutable": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz", + "integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==" + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -28084,6 +28559,17 @@ } } }, + "popmotion": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", + "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", + "requires": { + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" + } + }, "postcss": { "version": "8.4.21", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", @@ -29055,6 +29541,35 @@ } } }, + "react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "requires": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + } + }, + "react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "requires": { + "dnd-core": "^16.0.1" + } + }, + "react-dnd-touch-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-touch-backend/-/react-dnd-touch-backend-16.0.1.tgz", + "integrity": "sha512-NonoCABzzjyWGZuDxSG77dbgMZ2Wad7eQiCd/ECtsR2/NBLTjGksPUx9UPezZ1nQ/L7iD130Tz3RUshL/ClKLA==", + "requires": { + "@react-dnd/invariant": "^4.0.1", + "dnd-core": "^16.0.1" + } + }, "react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -29181,6 +29696,15 @@ "yallist": "^4.0.0" } }, + "sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, "semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", @@ -29207,6 +29731,14 @@ "prop-types": "^15.6.2" } }, + "react-use-measure": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz", + "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==", + "requires": { + "debounce": "^1.2.1" + } + }, "reactflow": { "version": "11.5.6", "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.5.6.tgz", @@ -29262,6 +29794,14 @@ "strip-indent": "^3.0.0" } }, + "redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -29539,10 +30079,20 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "sass": { + "version": "1.58.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.3.tgz", + "integrity": "sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==", + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, "sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", + "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==", "requires": { "klona": "^2.0.4", "neo-async": "^2.6.2" @@ -30016,6 +30566,15 @@ "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", "requires": {} }, + "style-value-types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", + "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", + "requires": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", diff --git a/typescript/Frontend/package.json b/typescript/Frontend/package.json index 0d166a2ae..a210524d3 100644 --- a/typescript/Frontend/package.json +++ b/typescript/Frontend/package.json @@ -5,6 +5,7 @@ "dependencies": { "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", + "@minoru/react-dnd-treeview": "^3.4.1", "@mui/icons-material": "^5.11.0", "@mui/lab": "^5.0.0-alpha.120", "@mui/material": "^5.11.7", @@ -18,14 +19,20 @@ "@types/react-router-dom": "^5.3.3", "axios": "^1.3.1", "chart.js": "^4.2.1", + "css-loader": "^6.7.3", "formik": "^2.2.9", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", "react-intl": "^6.2.10", "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", "reactflow": "^11.5.6", + "sass": "^1.58.3", + "sass-loader": "^13.2.0", + "style-loader": "^3.3.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, diff --git a/typescript/Frontend/src/components/Groups/DragPreview.module.scss b/typescript/Frontend/src/components/Groups/DragPreview.module.scss new file mode 100644 index 000000000..54a9c3672 --- /dev/null +++ b/typescript/Frontend/src/components/Groups/DragPreview.module.scss @@ -0,0 +1,20 @@ +.root { + align-items: "center"; + background-color: #1967d2; + border-radius: 4px; + box-shadow: 0 12px 24px -6px rgba(0, 0, 0, 0.25), + 0 0 0 1px rgba(0, 0, 0, 0.08); + color: #fff; + display: inline-grid; + font-size: 14px; + gap: 8px; + grid-template-columns: auto auto; + padding: 4px 8px; + pointer-events: none; +} + +.icon, +.label { + align-items: center; + display: flex; +} diff --git a/typescript/Frontend/src/components/Groups/DragPreview.tsx b/typescript/Frontend/src/components/Groups/DragPreview.tsx new file mode 100644 index 000000000..030ee14d9 --- /dev/null +++ b/typescript/Frontend/src/components/Groups/DragPreview.tsx @@ -0,0 +1,23 @@ +import { DragLayerMonitorProps } from "@minoru/react-dnd-treeview"; +import { I_Folder, I_Installation } from "../../util/types"; +import TypeIcon from "./TypeIcon"; +import styles from "./DragPreview.module.scss"; + +interface DragPreviewProps { + monitorProps: DragLayerMonitorProps; +} + +const DragPreview = (props: DragPreviewProps) => { + const item = props.monitorProps.item; + + return ( +
+
+ +
+
{item.text}
+
+ ); +}; + +export default DragPreview; diff --git a/typescript/Frontend/src/components/Groups/Group.tsx b/typescript/Frontend/src/components/Groups/Folder.tsx similarity index 84% rename from typescript/Frontend/src/components/Groups/Group.tsx rename to typescript/Frontend/src/components/Groups/Folder.tsx index 7ee75835d..3215d7564 100644 --- a/typescript/Frontend/src/components/Groups/Group.tsx +++ b/typescript/Frontend/src/components/Groups/Folder.tsx @@ -4,8 +4,9 @@ import { useState, useEffect } from "react"; import { useParams } from "react-router-dom"; import axiosConfig from "../../config/axiosConfig"; import { I_Installation } from "../../util/types"; +import FolderForm from "./FolderForm"; -const Group = () => { +const Folder = () => { const { id } = useParams(); const [values, setValues] = useState(); const [loading, setLoading] = useState(false); @@ -26,7 +27,13 @@ const Group = () => { }, [id]); if (values && values.id && values.id.toString() === id) { - return {id}; + return ( + <> + + + + + ); } else if (loading) { return ( { return null; }; -export default Group; +export default Folder; diff --git a/typescript/Frontend/src/components/Groups/FolderForm.tsx b/typescript/Frontend/src/components/Groups/FolderForm.tsx new file mode 100644 index 000000000..0f5d49fc2 --- /dev/null +++ b/typescript/Frontend/src/components/Groups/FolderForm.tsx @@ -0,0 +1,89 @@ +import { Alert, Button, Grid, Snackbar } from "@mui/material"; +import { useFormik } from "formik"; +import { useState } from "react"; +import { FormattedMessage, useIntl } from "react-intl"; +import axiosConfig from "../../config/axiosConfig"; +import { I_Folder } from "../../util/types"; +import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; + +interface I_CustomerFormProps { + values: I_Folder; + id: string | undefined; +} + +const updateFolder = (data: any) => { + return axiosConfig.put("/UpdateFolder", data); +}; +const FolderForm = (props: I_CustomerFormProps) => { + const { values, id } = props; + const intl = useIntl(); + + const [snackbarOpen, setSnackbarOpen] = useState(false); + + const formik = useFormik({ + initialValues: { + name: values.name, + information: values.information, + }, + onSubmit: (formikValues) => { + updateFolder({ + ...formikValues, + id, + }).then((res) => { + setSnackbarOpen(true); + }); + }, + }); + + const handleClose = () => { + setSnackbarOpen(false); + }; + + return ( +
+ + + + + + + + + + + + ); +}; + +export default FolderForm; diff --git a/typescript/Frontend/src/components/Groups/GroupTabs.tsx b/typescript/Frontend/src/components/Groups/GroupTabs.tsx index a279982e9..06360665c 100644 --- a/typescript/Frontend/src/components/Groups/GroupTabs.tsx +++ b/typescript/Frontend/src/components/Groups/GroupTabs.tsx @@ -9,8 +9,9 @@ import { useIntl } from "react-intl"; const GroupTabs = () => { const routeMatch = useRouteMatch([ - routes.groups + routes.group + ":id", + routes.groups + routes.folder + ":id", routes.groups + routes.users + ":id", + routes.groups + routes.installation + ":id", ]); const id = routeMatch?.params?.id; @@ -19,19 +20,29 @@ const GroupTabs = () => { return ( - - + + {routeMatch?.pathname.includes("folder") ? ( + + ) : ( + + )} + [] => { + return data.map((element) => { + const isFolder = element.type === "Folder"; + return { + id: isFolder ? element.id : "installation-" + element.id, + parent: element.parentId, + text: element.name, + droppable: isFolder, + data: element, + }; + }); +}; + const GroupTree = () => { const [data, setData] = useState<(I_Folder | I_Installation)[]>(); + const [loading, setLoading] = useState(false); useEffect(() => { - axiosConfig.get("/GetTree").then((res) => { - setData(res.data); - }); + getData(); }, []); - const instanceOfFolder = (object: any): object is I_Folder => { - return "children" in object; - }; - - const getNodes = (element: I_Folder | I_Installation): null | ReactNode => { - if (instanceOfFolder(element)) { - return element.children ? renderTree(element.children) : null; - } - return null; - }; - - const renderTree = (data: (I_Folder | I_Installation)[]): ReactNode => { - return data.map((element) => { - return ( - - - {getNodes(element)} - - - ); + const getData = async () => { + setLoading(true); + return axiosConfig.get("/GetAllFoldersAndInstallations").then((res) => { + setData(res.data); + setLoading(false); }); }; - return ( - } - defaultExpandIcon={} - sx={{ height: 300, flexGrow: 1, maxWidth: 400 }} - > - {data && renderTree(data)} - - ); + const handleDrop = ( + newTree: NodeModel[], + { dropTargetId, dragSource }: DropOptions + ) => { + axiosConfig + .put("/UpdateFolder", { + ...dragSource?.data, + parentId: dropTargetId, + }) + .then(() => { + getData(); + }); + }; + + if (loading) { + return ( + + + + ); + } else if (data && data?.length > 1) { + return ( + + + tree={getTreeData(data)} + rootId={0} + dragPreviewRender={(monitorProps) => ( + + )} + classes={{ + container: styles.tree, + root: styles.treeRoot, + draggingSource: styles.draggingSource, + dropTarget: styles.dropTarget, + }} + render={( + node: NodeModel, + { depth, isOpen, onToggle, hasChild } + ) => ( + + )} + onDrop={( + tree: NodeModel[], + options: DropOptions + ) => handleDrop(tree, options)} + /> + + ); + } + return null; }; export default GroupTree; diff --git a/typescript/Frontend/src/components/Groups/Groups.tsx b/typescript/Frontend/src/components/Groups/Groups.tsx index 77c062ca9..2f56c2850 100644 --- a/typescript/Frontend/src/components/Groups/Groups.tsx +++ b/typescript/Frontend/src/components/Groups/Groups.tsx @@ -2,8 +2,9 @@ import { Grid } from "@mui/material"; import { Container } from "@mui/system"; import { Routes, Route } from "react-router"; import routes from "../../routes.json"; +import InstallationDetail from "../Installations/Installation"; import NavigationButtons from "../Layout/NavigationButtons"; -import Group from "./Group"; +import Folder from "./Folder"; import GroupTabs from "./GroupTabs"; import GroupTree from "./GroupTree"; @@ -18,8 +19,12 @@ const Groups = () => { - } index /> + } index /> Users} /> + } + /> diff --git a/typescript/Frontend/src/components/Groups/TreeNode.module.scss b/typescript/Frontend/src/components/Groups/TreeNode.module.scss new file mode 100644 index 000000000..4f8b0f0c8 --- /dev/null +++ b/typescript/Frontend/src/components/Groups/TreeNode.module.scss @@ -0,0 +1,27 @@ +.root { + align-items: center; + display: grid; + grid-template-columns: auto auto 1fr auto; + height: 32px; + padding-inline-end: 8px; +} + +.expandIconWrapper { + align-items: center; + font-size: 0; + cursor: pointer; + display: flex; + height: 24px; + justify-content: center; + width: 24px; + transition: transform linear 0.1s; + transform: rotate(0deg); +} + +.expandIconWrapper.isOpen { + transform: rotate(90deg); +} + +.labelGridItem { + padding-inline-start: 8px; +} diff --git a/typescript/Frontend/src/components/Groups/TreeNode.tsx b/typescript/Frontend/src/components/Groups/TreeNode.tsx new file mode 100644 index 000000000..dc1f50d25 --- /dev/null +++ b/typescript/Frontend/src/components/Groups/TreeNode.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import Typography from "@mui/material/Typography"; +import ArrowRightIcon from "@mui/icons-material/ArrowRight"; +import { NodeModel } from "@minoru/react-dnd-treeview"; +import styles from "./TreeNode.module.scss"; +import { Link } from "react-router-dom"; +import routes from "../../routes.json"; +import { I_Folder, I_Installation } from "../../util/types"; +import TypeIcon from "./TypeIcon"; + +type Props = { + node: NodeModel; + depth: number; + isOpen: boolean; + onToggle: (id: NodeModel["id"]) => void; + hasChild: boolean; +}; + +const TreeNode: React.FC = (props) => { + const { node, isOpen, hasChild, onToggle, depth } = props; + const indent = depth * 24; + + const handleToggle = (e: React.MouseEvent) => { + e.stopPropagation(); + onToggle(node.id); + }; + + return ( +
+
+ {node.droppable && hasChild && ( +
+ +
+ )} +
+ + +
+ {node.text} +
+ +
+ ); +}; + +export default TreeNode; diff --git a/typescript/Frontend/src/components/Groups/TypeIcon.tsx b/typescript/Frontend/src/components/Groups/TypeIcon.tsx new file mode 100644 index 000000000..c86b4afd7 --- /dev/null +++ b/typescript/Frontend/src/components/Groups/TypeIcon.tsx @@ -0,0 +1,15 @@ +import FolderIcon from "@mui/icons-material/Folder"; +import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; + +interface TypeIconProps { + type: string | undefined; +} +const TypeIcon = (props: TypeIconProps) => { + return ( +
+ {props.type === "Folder" ? : } +
+ ); +}; + +export default TypeIcon; diff --git a/typescript/Frontend/src/lang/de.json b/typescript/Frontend/src/lang/de.json index 6af37ed1d..80d807f0f 100644 --- a/typescript/Frontend/src/lang/de.json +++ b/typescript/Frontend/src/lang/de.json @@ -16,5 +16,6 @@ "logout": "Logout", "updatedSuccessfully": "Erfolgreich aktualisiert", "groups": "Gruppen", - "group": "Gruppe" + "group": "Gruppe", + "folder": "Ordner" } diff --git a/typescript/Frontend/src/lang/en.json b/typescript/Frontend/src/lang/en.json index a0d76620b..856e907aa 100644 --- a/typescript/Frontend/src/lang/en.json +++ b/typescript/Frontend/src/lang/en.json @@ -16,5 +16,7 @@ "logout": "Logout", "updatedSuccessfully": "Updated successfully", "groups": "Groups", - "group": "Group" + "group": "Group", + "folder": "folder" + } diff --git a/typescript/Frontend/src/routes.json b/typescript/Frontend/src/routes.json index 0aba4fef1..619e97e29 100644 --- a/typescript/Frontend/src/routes.json +++ b/typescript/Frontend/src/routes.json @@ -5,5 +5,6 @@ "log": "log/", "installations": "/installations/", "groups": "/groups/", - "group": "group/" + "group": "group/", + "folder": "folder/" }