[WIP] create tree view with drag and drop functionality

This commit is contained in:
Sina Blattmann 2023-03-09 10:50:56 +01:00
parent 19978dd211
commit 53346740ab
16 changed files with 971 additions and 76 deletions

View File

@ -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",

View File

@ -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"
},

View File

@ -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;
}

View File

@ -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<I_Installation | I_Folder>;
}
const DragPreview = (props: DragPreviewProps) => {
const item = props.monitorProps.item;
return (
<div className={styles.root}>
<div className={styles.icon}>
<TypeIcon type={item.data?.type} />
</div>
<div className={styles.label}>{item.text}</div>
</div>
);
};
export default DragPreview;

View File

@ -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<I_Installation>();
const [loading, setLoading] = useState(false);
@ -26,7 +27,13 @@ const Group = () => {
}, [id]);
if (values && values.id && values.id.toString() === id) {
return <Box sx={{ py: 3 }}>{id}</Box>;
return (
<>
<Box sx={{ py: 3 }}>
<FolderForm values={values} id={id} />
</Box>
</>
);
} else if (loading) {
return (
<Box
@ -45,4 +52,4 @@ const Group = () => {
return null;
};
export default Group;
export default Folder;

View File

@ -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 (
<form onSubmit={formik.handleSubmit}>
<InnovenergyTextfield
id="name-textfield"
label={intl.formatMessage({
id: "name",
defaultMessage: "Name",
})}
name="name"
value={formik.values.name}
handleChange={formik.handleChange}
/>
<InnovenergyTextfield
id="information-textfield"
label={intl.formatMessage({
id: "information",
defaultMessage: "Information",
})}
name="information"
value={formik.values.information}
handleChange={formik.handleChange}
/>
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
<Button variant="outlined" type="submit">
<FormattedMessage id="applyChanges" defaultMessage="Apply changes" />
</Button>
</Grid>
<Snackbar
open={snackbarOpen}
anchorOrigin={{
vertical: "top",
horizontal: "center",
}}
autoHideDuration={6000}
onClose={handleClose}
>
<Alert onClose={handleClose} severity="success" sx={{ width: "100%" }}>
<FormattedMessage
id="updatedSuccessfully"
defaultMessage="Updated successfully"
/>
</Alert>
</Snackbar>
</form>
);
};
export default FolderForm;

View File

@ -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 (
<Box sx={{ width: "100%" }}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs
value={routeMatch?.pattern?.path ?? routes.group + ":id"}
aria-label="basic tabs example"
>
<Tabs value={routeMatch?.pattern?.path} aria-label="basic tabs example">
{routeMatch?.pathname.includes("folder") ? (
<Tab
label={intl.formatMessage({
id: "group",
defaultMessage: "Group",
id: "folder",
defaultMessage: "Folder",
})}
value={routes.groups + routes.group + ":id"}
value={routes.groups + routes.folder + ":id"}
component={Link}
to={routes.group + id}
to={routes.folder + id}
/>
) : (
<Tab
label={intl.formatMessage({
id: "installation",
defaultMessage: "Installation",
})}
value={routes.groups + routes.installation + ":id"}
component={Link}
to={routes.installation + id}
/>
)}
<Tab
label={intl.formatMessage({
id: "users",

View File

@ -0,0 +1,20 @@
.tree {
list-style-type: none;
padding-left: 0;
}
.app {
height: 100%;
}
.treeRoot {
height: 100%;
}
.draggingSource {
opacity: 0.3;
}
.dropTarget {
background-color: #e8f0fe;
}

View File

@ -1,60 +1,106 @@
import TreeView from "@mui/lab/TreeView";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import { ReactNode, useEffect, useState } from "react";
import { TreeItem } from "@mui/lab";
import { Link } from "react-router-dom";
import { useEffect, useState } from "react";
import axiosConfig from "../../config/axiosConfig";
import { I_Folder, I_Installation } from "../../util/types";
const GroupTree = () => {
const [data, setData] = useState<(I_Folder | I_Installation)[]>();
import { CircularProgress, Grid } from "@mui/material";
import { DndProvider } from "react-dnd";
import {
MultiBackend,
getBackendOptions,
Tree,
NodeModel,
DropOptions,
} from "@minoru/react-dnd-treeview";
import TreeNode from "./TreeNode";
import styles from "./GroupTree.module.scss";
import DragPreview from "./DragPreview";
useEffect(() => {
axiosConfig.get("/GetTree").then((res) => {
setData(res.data);
const getTreeData = (
data: (I_Folder | I_Installation)[]
): NodeModel<I_Folder | I_Installation>[] => {
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 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;
const GroupTree = () => {
const [data, setData] = useState<(I_Folder | I_Installation)[]>();
const [loading, setLoading] = useState(false);
useEffect(() => {
getData();
}, []);
const getData = async () => {
setLoading(true);
return axiosConfig.get("/GetAllFoldersAndInstallations").then((res) => {
setData(res.data);
setLoading(false);
});
};
const handleDrop = (
newTree: NodeModel<I_Folder | I_Installation>[],
{ dropTargetId, dragSource }: DropOptions<I_Folder | I_Installation>
) => {
axiosConfig
.put("/UpdateFolder", {
...dragSource?.data,
parentId: dropTargetId,
})
.then(() => {
getData();
});
};
if (loading) {
return (
<Grid container justifyContent="center" width="100%">
<CircularProgress sx={{ m: 6 }} />
</Grid>
);
} else if (data && data?.length > 1) {
return (
<DndProvider backend={MultiBackend} options={getBackendOptions()}>
<Tree<I_Installation | I_Folder>
tree={getTreeData(data)}
rootId={0}
dragPreviewRender={(monitorProps) => (
<DragPreview monitorProps={monitorProps} />
)}
classes={{
container: styles.tree,
root: styles.treeRoot,
draggingSource: styles.draggingSource,
dropTarget: styles.dropTarget,
}}
render={(
node: NodeModel<I_Installation | I_Folder>,
{ depth, isOpen, onToggle, hasChild }
) => (
<TreeNode
node={node}
depth={depth}
isOpen={isOpen}
onToggle={onToggle}
hasChild={hasChild}
/>
)}
onDrop={(
tree: NodeModel<I_Folder | I_Installation>[],
options: DropOptions<I_Folder | I_Installation>
) => handleDrop(tree, options)}
/>
</DndProvider>
);
}
return null;
};
const renderTree = (data: (I_Folder | I_Installation)[]): ReactNode => {
return data.map((element) => {
return (
<Link
to={"/groups/group/" + element.id}
style={{ textDecoration: "none", color: "black" }}
>
<TreeItem
key={element.id}
nodeId={element.id.toString()}
label={element.name}
>
{getNodes(element)}
</TreeItem>
</Link>
);
});
};
return (
<TreeView
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
sx={{ height: 300, flexGrow: 1, maxWidth: 400 }}
>
{data && renderTree(data)}
</TreeView>
);
};
export default GroupTree;

View File

@ -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 = () => {
<Grid item xs={9}>
<GroupTabs />
<Routes>
<Route path={routes.group + ":id"} element={<Group />} index />
<Route path={routes.folder + ":id"} element={<Folder />} index />
<Route path={routes.users + ":id"} element={<div>Users</div>} />
<Route
path={routes.installation + ":id"}
element={<InstallationDetail />}
/>
</Routes>
</Grid>
</Grid>

View File

@ -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;
}

View File

@ -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<I_Installation | I_Folder>;
depth: number;
isOpen: boolean;
onToggle: (id: NodeModel["id"]) => void;
hasChild: boolean;
};
const TreeNode: React.FC<Props> = (props) => {
const { node, isOpen, hasChild, onToggle, depth } = props;
const indent = depth * 24;
const handleToggle = (e: React.MouseEvent) => {
e.stopPropagation();
onToggle(node.id);
};
return (
<div
className={`tree-node ${styles.root}`}
style={{ paddingInlineStart: indent }}
>
<div
className={`${styles.expandIconWrapper} ${isOpen ? styles.isOpen : ""}`}
>
{node.droppable && hasChild && (
<div onClick={handleToggle}>
<ArrowRightIcon />
</div>
)}
</div>
<TypeIcon type={node.data?.type} />
<Link
to={
node.droppable
? routes.folder + node.id
: routes.installation + (node.data ? node.data.id : "")
}
style={{
textDecoration: "none",
color: "black",
}}
>
<div className={styles.labelGridItem}>
<Typography variant="body2">{node.text}</Typography>
</div>
</Link>
</div>
);
};
export default TreeNode;

View File

@ -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 (
<div>
{props.type === "Folder" ? <FolderIcon /> : <InsertDriveFileIcon />}
</div>
);
};
export default TypeIcon;

View File

@ -16,5 +16,6 @@
"logout": "Logout",
"updatedSuccessfully": "Erfolgreich aktualisiert",
"groups": "Gruppen",
"group": "Gruppe"
"group": "Gruppe",
"folder": "Ordner"
}

View File

@ -16,5 +16,7 @@
"logout": "Logout",
"updatedSuccessfully": "Updated successfully",
"groups": "Groups",
"group": "Group"
"group": "Group",
"folder": "folder"
}

View File

@ -5,5 +5,6 @@
"log": "log/",
"installations": "/installations/",
"groups": "/groups/",
"group": "group/"
"group": "group/",
"folder": "folder/"
}