Created ER diagram screen #9
|
|
@ -8,6 +8,9 @@
|
|||
"name": "qbx",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@mui/material": "^7.1.1",
|
||||
"@univerjs-pro/engine-pivot": "^0.7.0",
|
||||
"@univerjs-pro/sheets-pivot": "^0.7.0",
|
||||
"@univerjs-pro/sheets-pivot-ui": "^0.7.0",
|
||||
|
|
@ -54,7 +57,6 @@
|
|||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
|
|
@ -110,7 +112,6 @@
|
|||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz",
|
||||
"integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.27.1",
|
||||
|
|
@ -144,7 +145,6 @@
|
|||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
|
||||
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.27.1",
|
||||
|
|
@ -186,7 +186,6 @@
|
|||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
|
@ -196,7 +195,6 @@
|
|||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
|
||||
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
|
@ -230,7 +228,6 @@
|
|||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz",
|
||||
"integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.27.1"
|
||||
|
|
@ -287,7 +284,6 @@
|
|||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
||||
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
|
|
@ -302,7 +298,6 @@
|
|||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz",
|
||||
"integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
|
|
@ -321,7 +316,6 @@
|
|||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
|
|
@ -331,7 +325,6 @@
|
|||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
|
||||
"integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
|
|
@ -341,6 +334,158 @@
|
|||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
|
||||
"integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.16.7",
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"convert-source-map": "^1.5.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"find-root": "^1.1.0",
|
||||
"source-map": "^0.5.7",
|
||||
"stylis": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/cache": {
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
|
||||
"integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"@emotion/weak-memoize": "^0.4.0",
|
||||
"stylis": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/hash": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
|
||||
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/is-prop-valid": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz",
|
||||
"integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/memoize": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/react": {
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/cache": "^11.14.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"@emotion/weak-memoize": "^0.4.0",
|
||||
"hoist-non-react-statics": "^3.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/serialize": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
|
||||
"integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/unitless": "^0.10.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/sheet": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
|
||||
"integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/styled": {
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz",
|
||||
"integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/is-prop-valid": "^1.3.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
|
||||
"@emotion/utils": "^1.4.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.0.0-rc.0",
|
||||
"react": ">=16.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/unitless": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
|
||||
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
|
||||
"integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/utils": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
|
||||
"integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/weak-memoize": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
|
||||
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz",
|
||||
|
|
@ -1064,7 +1209,6 @@
|
|||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
||||
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
|
|
@ -1079,7 +1223,6 @@
|
|||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
|
|
@ -1089,7 +1232,6 @@
|
|||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
|
|
@ -1099,14 +1241,12 @@
|
|||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
|
|
@ -1146,6 +1286,225 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.1.tgz",
|
||||
"integrity": "sha512-yBckQs4aQ8mqukLnPC6ivIRv6guhaXi8snVl00VtyojBbm+l6VbVhyTSZ68Abcx7Ah8B+GZhrB7BOli+e+9LkQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-7.1.1.tgz",
|
||||
"integrity": "sha512-mTpdmdZCaHCGOH3SrYM41+XKvNL0iQfM9KlYgpSjgadXx/fEKhhvOktxm8++Xw6FFeOHoOiV+lzOI8X1rsv71A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@mui/core-downloads-tracker": "^7.1.1",
|
||||
"@mui/system": "^7.1.1",
|
||||
"@mui/types": "^7.4.3",
|
||||
"@mui/utils": "^7.1.1",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@types/react-transition-group": "^4.4.12",
|
||||
"clsx": "^2.1.1",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^19.1.0",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material-pigment-css": "^7.1.1",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
},
|
||||
"@mui/material-pigment-css": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material/node_modules/react-is": {
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
|
||||
"integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@mui/private-theming": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.1.1.tgz",
|
||||
"integrity": "sha512-M8NbLUx+armk2ZuaxBkkMk11ultnWmrPlN0Xe3jUEaBChg/mcxa5HWIWS1EE4DF36WRACaAHVAvyekWlDQf0PQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@mui/utils": "^7.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/styled-engine": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.1.1.tgz",
|
||||
"integrity": "sha512-R2wpzmSN127j26HrCPYVQ53vvMcT5DaKLoWkrfwUYq3cYytL6TQrCH8JBH3z79B6g4nMZZVoaXrxO757AlShaw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@emotion/cache": "^11.13.5",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/system": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-7.1.1.tgz",
|
||||
"integrity": "sha512-Kj1uhiqnj4Zo7PDjAOghtXJtNABunWvhcRU0O7RQJ7WOxeynoH6wXPcilphV8QTFtkKaip8EiNJRiCD+B3eROA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@mui/private-theming": "^7.1.1",
|
||||
"@mui/styled-engine": "^7.1.1",
|
||||
"@mui/types": "^7.4.3",
|
||||
"@mui/utils": "^7.1.1",
|
||||
"clsx": "^2.1.1",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/types": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.3.tgz",
|
||||
"integrity": "sha512-2UCEiK29vtiZTeLdS2d4GndBKacVyxGvReznGXGr+CzW/YhjIX+OHUdCIczZjzcRAgKBGmE9zCIgoV9FleuyRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/utils": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.1.tgz",
|
||||
"integrity": "sha512-BkOt2q7MBYl7pweY2JWwfrlahhp+uGLR8S+EhiyRaofeRYUWL2YKbSGQvN4hgSN1i8poN0PaUiii1kEMrchvzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.1",
|
||||
"@mui/types": "^7.4.3",
|
||||
"@types/prop-types": "^15.7.14",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^19.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/utils/node_modules/react-is": {
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
|
||||
"integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@noble/ed25519": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.2.3.tgz",
|
||||
|
|
@ -1167,6 +1526,16 @@
|
|||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
|
|
@ -2854,6 +3223,18 @@
|
|||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
||||
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
||||
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz",
|
||||
|
|
@ -2885,6 +3266,15 @@
|
|||
"redux": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
"version": "4.4.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
|
||||
"integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/resize-observer-browser": {
|
||||
"version": "0.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.11.tgz",
|
||||
|
|
@ -5234,6 +5624,21 @@
|
|||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"resolve": "^1.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
|
@ -5350,7 +5755,6 @@
|
|||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
|
|
@ -5575,6 +5979,31 @@
|
|||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig/node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
|
|
@ -6022,7 +6451,6 @@
|
|||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
|
|
@ -6182,6 +6610,15 @@
|
|||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
|
|
@ -6288,7 +6725,6 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
|
|
@ -6664,6 +7100,12 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/find-root": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
|
||||
|
|
@ -7038,7 +7480,6 @@
|
|||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parent-module": "^1.0.0",
|
||||
|
|
@ -7087,6 +7528,27 @@
|
|||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
|
|
@ -7157,7 +7619,6 @@
|
|||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
||||
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jsesc": "bin/jsesc"
|
||||
|
|
@ -7173,6 +7634,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
|
|
@ -7239,6 +7706,12 @@
|
|||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/localforage": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
|
||||
|
|
@ -7427,7 +7900,6 @@
|
|||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/n-gram": {
|
||||
|
|
@ -7637,7 +8109,6 @@
|
|||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"callsites": "^3.0.0"
|
||||
|
|
@ -7646,6 +8117,24 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
"json-parse-even-better-errors": "^2.3.0",
|
||||
"lines-and-columns": "^1.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
|
|
@ -7676,6 +8165,12 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
||||
|
|
@ -7686,11 +8181,19 @@
|
|||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"devOptional": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
|
|
@ -8412,11 +8915,30 @@
|
|||
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.16.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
|
|
@ -8701,6 +9223,15 @@
|
|||
"react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
|
|
@ -8768,6 +9299,12 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
|
|
@ -8781,6 +9318,18 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.0.tgz",
|
||||
|
|
@ -9225,6 +9774,21 @@
|
|||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
|
||||
"integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.6"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@
|
|||
"copy-assets": "npm run copy-luckysheet-assets && npm run copy-univerjs-assets"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@mui/material": "^7.1.1",
|
||||
"@univerjs-pro/engine-pivot": "^0.7.0",
|
||||
"@univerjs-pro/sheets-pivot": "^0.7.0",
|
||||
"@univerjs-pro/sheets-pivot-ui": "^0.7.0",
|
||||
|
|
|
|||
115
src/App.jsx
115
src/App.jsx
|
|
@ -3,7 +3,31 @@ import './App.css'
|
|||
import InfiniteCanvas from './components/InfiniteCanvas'
|
||||
import AdvancedCharts from './components/AdvancedCharts'
|
||||
import DataflowCanvas from './components/DataflowCanvas'
|
||||
import ERDiagramCanvas from './components/ERDiagramCanvas'
|
||||
import { FaDatabase, FaChartBar, FaProjectDiagram, FaSitemap, FaFolder, FaCog, FaChevronDown, FaChevronRight, FaTachometerAlt } from 'react-icons/fa'
|
||||
import { Breadcrumbs, Link, Typography } from '@mui/material'
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles'
|
||||
|
||||
// Create a custom Material UI theme to match your application's dark theme
|
||||
const darkTheme = createTheme({
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
primary: {
|
||||
main: '#00a99d',
|
||||
},
|
||||
text: {
|
||||
primary: '#ffffff',
|
||||
secondary: '#aaaaaa',
|
||||
},
|
||||
background: {
|
||||
default: '#121212',
|
||||
paper: '#1a1a1a',
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
|
||||
},
|
||||
});
|
||||
|
||||
function App() {
|
||||
// Initialize activeTab based on URL pathname if present
|
||||
|
|
@ -92,6 +116,7 @@ function App() {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<div className="app-container" style={{
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
|
|
@ -392,6 +417,43 @@ function App() {
|
|||
}}>
|
||||
{activeTab === 'canvas' && (
|
||||
<>
|
||||
<div style={{
|
||||
marginBottom: '20px'
|
||||
}}>
|
||||
<h1 style={{
|
||||
fontSize: '28px',
|
||||
fontWeight: '600',
|
||||
margin: '0 0 8px 0',
|
||||
color: '#ffffff'
|
||||
}}>
|
||||
Overview
|
||||
</h1>
|
||||
<Breadcrumbs
|
||||
aria-label="breadcrumb"
|
||||
separator="›"
|
||||
sx={{
|
||||
'& .MuiBreadcrumbs-separator': {
|
||||
color: '#666',
|
||||
margin: '0 8px',
|
||||
},
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
underline="hover"
|
||||
color="primary"
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setIsDropdownOpen(true); // Ensure dropdown is open
|
||||
}}
|
||||
sx={{ fontWeight: 500 }}
|
||||
>
|
||||
Qubit
|
||||
</Link>
|
||||
<Typography color="text.secondary">Overview</Typography>
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
<InfiniteCanvas />
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
|
|
@ -497,58 +559,25 @@ function App() {
|
|||
|
||||
{activeTab === 'er_diagram' && (
|
||||
<>
|
||||
<DataflowCanvas
|
||||
key={`er-diagram-${currentDbSlug || 'default'}-${hasSchemas ? 'has-schemas' : 'no-schemas'}-${Date.now()}`}
|
||||
dbSlug={currentDbSlug}
|
||||
hasSchemas={hasSchemas}
|
||||
mode="er_diagram"
|
||||
/>
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
bottom: '20px',
|
||||
left: '20px',
|
||||
background: 'rgba(26, 26, 26, 0.8)',
|
||||
padding: '12px 16px',
|
||||
borderRadius: '8px',
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
|
||||
fontSize: '12px',
|
||||
pointerEvents: 'none',
|
||||
backdropFilter: 'blur(5px)',
|
||||
border: '1px solid rgba(138, 43, 226, 0.3)',
|
||||
color: '#ffffff',
|
||||
maxWidth: '300px',
|
||||
animation: 'fadeIn 0.5s ease'
|
||||
marginBottom: '20px'
|
||||
}}>
|
||||
<p style={{
|
||||
margin: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontSize: '13px'
|
||||
<h1 style={{
|
||||
fontSize: '28px',
|
||||
fontWeight: '600',
|
||||
margin: '0 0 8px 0',
|
||||
color: '#ffffff'
|
||||
}}>
|
||||
<span style={{
|
||||
display: 'inline-block',
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
background: '#8a2be2',
|
||||
borderRadius: '50%',
|
||||
marginRight: '8px',
|
||||
boxShadow: '0 0 8px #8a2be2'
|
||||
}}></span>
|
||||
Entity Relationship Diagram
|
||||
</p>
|
||||
<p style={{
|
||||
margin: '5px 0 0 16px',
|
||||
fontSize: '11px',
|
||||
opacity: 0.7
|
||||
}}>
|
||||
View table relationships • Explore schema structure • Analyze foreign keys
|
||||
</p>
|
||||
ER Diagram
|
||||
</h1>
|
||||
</div>
|
||||
<ERDiagramCanvas />
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M19,3 L5,3 C3.9,3 3,3.9 3,5 L3,19 C3,20.1 3.9,21 5,21 L19,21 C20.1,21 21,20.1 21,19 L21,5 C21,3.9 20.1,3 19,3 Z M16,13 L13,13 L13,16 C13,16.55 12.55,17 12,17 C11.45,17 11,16.55 11,16 L11,13 L8,13 C7.45,13 7,12.55 7,12 C7,11.45 7.45,11 8,11 L11,11 L11,8 C11,7.45 11.45,7 12,7 C12.55,7 13,7.45 13,8 L13,11 L16,11 C16.55,11 17,11.45 17,12 C17,12.55 16.55,13 16,13 Z" fill="#00a99d" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 651 B |
Binary file not shown.
|
After Width: | Height: | Size: 407 KiB |
|
|
@ -0,0 +1,707 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
TextField,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
IconButton,
|
||||
Box,
|
||||
Typography,
|
||||
Divider,
|
||||
Grid,
|
||||
Paper,
|
||||
Chip,
|
||||
Alert
|
||||
} from '@mui/material';
|
||||
import {
|
||||
FaPlus as AddIcon,
|
||||
FaTrash as DeleteIcon,
|
||||
FaTimes as CloseIcon,
|
||||
FaTable as TableIcon,
|
||||
FaKey as KeyIcon,
|
||||
FaLink as LinkIcon
|
||||
} from 'react-icons/fa';
|
||||
|
||||
const AddTableModal = ({
|
||||
open,
|
||||
onClose,
|
||||
onAddTable,
|
||||
schemas = [],
|
||||
existingTables = [],
|
||||
tableTypes = [
|
||||
{ value: 'fact', label: 'Fact Table' },
|
||||
{ value: 'dimension', label: 'Dimension Table' },
|
||||
{ value: 'stage', label: 'Stage Table' }
|
||||
],
|
||||
columnTypes = [
|
||||
'INTEGER',
|
||||
'VARCHAR(255)',
|
||||
'VARCHAR(100)',
|
||||
'VARCHAR(50)',
|
||||
'TEXT',
|
||||
'DECIMAL(10,2)',
|
||||
'DECIMAL(15,2)',
|
||||
'DATE',
|
||||
'TIMESTAMP',
|
||||
'BOOLEAN',
|
||||
'BIGINT',
|
||||
'SMALLINT',
|
||||
'FLOAT',
|
||||
'DOUBLE'
|
||||
]
|
||||
}) => {
|
||||
// Main form state
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
description: '',
|
||||
tableType: '',
|
||||
schema: ''
|
||||
});
|
||||
|
||||
// Dynamic sections state
|
||||
const [columns, setColumns] = useState([]);
|
||||
const [keys, setKeys] = useState([]);
|
||||
const [relations, setRelations] = useState([]);
|
||||
|
||||
// Validation and UI state
|
||||
const [errors, setErrors] = useState({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Reset form when modal opens/closes
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
resetForm();
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const resetForm = () => {
|
||||
setFormData({
|
||||
name: '',
|
||||
description: '',
|
||||
tableType: '',
|
||||
schema: ''
|
||||
});
|
||||
setColumns([]);
|
||||
setKeys([]);
|
||||
setRelations([]);
|
||||
setErrors({});
|
||||
setIsSubmitting(false);
|
||||
};
|
||||
|
||||
// Form field handlers
|
||||
const handleFormChange = (field, value) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[field]: value
|
||||
}));
|
||||
// Clear error when user starts typing
|
||||
if (errors[field]) {
|
||||
setErrors(prev => ({
|
||||
...prev,
|
||||
[field]: null
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// Column management
|
||||
const addColumn = () => {
|
||||
const newColumn = {
|
||||
id: Date.now(),
|
||||
name: '',
|
||||
type: 'VARCHAR(255)',
|
||||
isPrimaryKey: false,
|
||||
isForeignKey: false,
|
||||
isNullable: true
|
||||
};
|
||||
setColumns(prev => [...prev, newColumn]);
|
||||
};
|
||||
|
||||
const updateColumn = (id, field, value) => {
|
||||
setColumns(prev => prev.map(col =>
|
||||
col.id === id ? { ...col, [field]: value } : col
|
||||
));
|
||||
};
|
||||
|
||||
const removeColumn = (id) => {
|
||||
setColumns(prev => prev.filter(col => col.id !== id));
|
||||
// Remove any keys that reference this column
|
||||
setKeys(prev => prev.filter(key => key.columnId !== id));
|
||||
};
|
||||
|
||||
// Key management
|
||||
const addKey = () => {
|
||||
const newKey = {
|
||||
id: Date.now(),
|
||||
name: '',
|
||||
columnId: '',
|
||||
sequence: 1,
|
||||
keyType: 'PRIMARY' // PRIMARY, FOREIGN, UNIQUE
|
||||
};
|
||||
setKeys(prev => [...prev, newKey]);
|
||||
};
|
||||
|
||||
const updateKey = (id, field, value) => {
|
||||
setKeys(prev => prev.map(key =>
|
||||
key.id === id ? { ...key, [field]: value } : key
|
||||
));
|
||||
};
|
||||
|
||||
const removeKey = (id) => {
|
||||
setKeys(prev => prev.filter(key => key.id !== id));
|
||||
};
|
||||
|
||||
// Relation management
|
||||
const addRelation = () => {
|
||||
const newRelation = {
|
||||
id: Date.now(),
|
||||
targetTable: '',
|
||||
sourceKey: '',
|
||||
targetKey: '',
|
||||
relationType: '1:N' // 1:1, 1:N, N:M
|
||||
};
|
||||
setRelations(prev => [...prev, newRelation]);
|
||||
};
|
||||
|
||||
const updateRelation = (id, field, value) => {
|
||||
setRelations(prev => prev.map(rel =>
|
||||
rel.id === id ? { ...rel, [field]: value } : rel
|
||||
));
|
||||
};
|
||||
|
||||
const removeRelation = (id) => {
|
||||
setRelations(prev => prev.filter(rel => rel.id !== id));
|
||||
};
|
||||
|
||||
// Get available keys for relations
|
||||
const getAvailableKeys = () => {
|
||||
return keys.map(key => {
|
||||
const column = columns.find(col => col.id === key.columnId);
|
||||
return {
|
||||
id: key.id,
|
||||
name: key.name,
|
||||
columnName: column?.name || 'Unknown Column'
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Get keys from selected target table
|
||||
const getTargetTableKeys = (tableId) => {
|
||||
const targetTable = existingTables.find(table => table.id === tableId);
|
||||
if (!targetTable || !targetTable.columns) return [];
|
||||
|
||||
return targetTable.columns
|
||||
.filter(col => col.is_primary_key || col.is_foreign_key)
|
||||
.map(col => ({
|
||||
id: col.name,
|
||||
name: col.name,
|
||||
type: col.is_primary_key ? 'PRIMARY' : 'FOREIGN'
|
||||
}));
|
||||
};
|
||||
|
||||
// Form validation
|
||||
const validateForm = () => {
|
||||
const newErrors = {};
|
||||
|
||||
if (!formData.name.trim()) {
|
||||
newErrors.name = 'Table name is required';
|
||||
}
|
||||
|
||||
if (!formData.schema) {
|
||||
newErrors.schema = 'Schema selection is required';
|
||||
}
|
||||
|
||||
if (!formData.tableType) {
|
||||
newErrors.tableType = 'Table type is required';
|
||||
}
|
||||
|
||||
// Validate columns
|
||||
const columnErrors = {};
|
||||
columns.forEach(col => {
|
||||
if (!col.name.trim()) {
|
||||
columnErrors[col.id] = 'Column name is required';
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(columnErrors).length > 0) {
|
||||
newErrors.columns = columnErrors;
|
||||
}
|
||||
|
||||
// Validate keys
|
||||
const keyErrors = {};
|
||||
keys.forEach(key => {
|
||||
if (!key.name.trim()) {
|
||||
keyErrors[key.id] = 'Key name is required';
|
||||
}
|
||||
if (!key.columnId) {
|
||||
keyErrors[key.id] = 'Column selection is required';
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(keyErrors).length > 0) {
|
||||
newErrors.keys = keyErrors;
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
// Form submission
|
||||
const handleSubmit = async () => {
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
// Prepare table data
|
||||
const tableData = {
|
||||
name: formData.name.trim(),
|
||||
description: formData.description.trim(),
|
||||
table_type: formData.tableType,
|
||||
schema: formData.schema,
|
||||
columns: columns.map(col => ({
|
||||
name: col.name.trim(),
|
||||
data_type: col.type,
|
||||
is_primary_key: keys.some(key => key.columnId === col.id && key.keyType === 'PRIMARY'),
|
||||
is_foreign_key: keys.some(key => key.columnId === col.id && key.keyType === 'FOREIGN'),
|
||||
is_nullable: col.isNullable
|
||||
})),
|
||||
keys: keys.map(key => ({
|
||||
name: key.name.trim(),
|
||||
column_name: columns.find(col => col.id === key.columnId)?.name || '',
|
||||
key_type: key.keyType,
|
||||
sequence: key.sequence
|
||||
})),
|
||||
relations: relations.map(rel => ({
|
||||
target_table: rel.targetTable,
|
||||
source_key: rel.sourceKey,
|
||||
target_key: rel.targetKey,
|
||||
relation_type: rel.relationType
|
||||
}))
|
||||
};
|
||||
|
||||
// Call the parent component's add table function
|
||||
await onAddTable(tableData);
|
||||
|
||||
// Close modal and reset form
|
||||
onClose();
|
||||
resetForm();
|
||||
} catch (error) {
|
||||
console.error('Error adding table:', error);
|
||||
setErrors({ submit: 'Failed to add table. Please try again.' });
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
maxWidth="lg"
|
||||
fullWidth
|
||||
PaperProps={{
|
||||
sx: {
|
||||
minHeight: '80vh',
|
||||
maxHeight: '90vh'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle sx={{ display: 'flex', alignItems: 'center', gap: 2, pb: 1 }}>
|
||||
<TableIcon color="primary" />
|
||||
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
|
||||
Add New Table
|
||||
</Typography>
|
||||
<IconButton onClick={onClose} size="small">
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent dividers sx={{ p: 3 }}>
|
||||
{errors.submit && (
|
||||
<Alert severity="error" sx={{ mb: 3 }}>
|
||||
{errors.submit}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Basic Table Information */}
|
||||
<Paper elevation={1} sx={{ p: 3, mb: 3 }}>
|
||||
<Typography variant="h6" gutterBottom color="primary">
|
||||
Table Information
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Table Name"
|
||||
value={formData.name}
|
||||
onChange={(e) => handleFormChange('name', e.target.value)}
|
||||
error={!!errors.name}
|
||||
helperText={errors.name}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<FormControl fullWidth error={!!errors.tableType} required>
|
||||
<InputLabel>Table Type</InputLabel>
|
||||
<Select
|
||||
value={formData.tableType}
|
||||
onChange={(e) => handleFormChange('tableType', e.target.value)}
|
||||
label="Table Type"
|
||||
>
|
||||
{tableTypes.map(type => (
|
||||
<MenuItem key={type.value} value={type.value}>
|
||||
{type.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<FormControl fullWidth error={!!errors.schema} required>
|
||||
<InputLabel>Schema</InputLabel>
|
||||
<Select
|
||||
value={formData.schema}
|
||||
onChange={(e) => handleFormChange('schema', e.target.value)}
|
||||
label="Schema"
|
||||
>
|
||||
{schemas.map(schema => (
|
||||
<MenuItem key={schema.sch} value={schema.sch}>
|
||||
{schema.name || schema.sch}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Description"
|
||||
value={formData.description}
|
||||
onChange={(e) => handleFormChange('description', e.target.value)}
|
||||
multiline
|
||||
rows={3}
|
||||
placeholder="Enter table description..."
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
|
||||
{/* Columns Section */}
|
||||
<Paper elevation={1} sx={{ p: 3, mb: 3 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||
<Typography variant="h6" color="primary">
|
||||
Columns
|
||||
</Typography>
|
||||
<Button
|
||||
startIcon={<AddIcon />}
|
||||
onClick={addColumn}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
Add Column
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{columns.length === 0 ? (
|
||||
<Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', py: 2 }}>
|
||||
No columns added yet. Click "Add Column" to get started.
|
||||
</Typography>
|
||||
) : (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
{columns.map((column) => (
|
||||
<Paper key={column.id} variant="outlined" sx={{ p: 2 }}>
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid item xs={12} md={4}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Column Name"
|
||||
value={column.name}
|
||||
onChange={(e) => updateColumn(column.id, 'name', e.target.value)}
|
||||
error={!!errors.columns?.[column.id]}
|
||||
helperText={errors.columns?.[column.id]}
|
||||
size="small"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={3}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>Data Type</InputLabel>
|
||||
<Select
|
||||
value={column.type}
|
||||
onChange={(e) => updateColumn(column.id, 'type', e.target.value)}
|
||||
label="Data Type"
|
||||
>
|
||||
{columnTypes.map(type => (
|
||||
<MenuItem key={type} value={type}>
|
||||
{type}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4}>
|
||||
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
|
||||
<Chip
|
||||
label="Nullable"
|
||||
variant={column.isNullable ? "filled" : "outlined"}
|
||||
size="small"
|
||||
onClick={() => updateColumn(column.id, 'isNullable', !column.isNullable)}
|
||||
color={column.isNullable ? "default" : "primary"}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={1}>
|
||||
<IconButton
|
||||
onClick={() => removeColumn(column.id)}
|
||||
color="error"
|
||||
size="small"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
|
||||
{/* Keys Section */}
|
||||
<Paper elevation={1} sx={{ p: 3, mb: 3 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||
<Typography variant="h6" color="primary">
|
||||
<KeyIcon style={{ marginRight: '8px', verticalAlign: 'middle' }} />
|
||||
Keys
|
||||
</Typography>
|
||||
<Button
|
||||
startIcon={<AddIcon />}
|
||||
onClick={addKey}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
disabled={columns.length === 0}
|
||||
>
|
||||
Add Key
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{keys.length === 0 ? (
|
||||
<Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', py: 2 }}>
|
||||
{columns.length === 0
|
||||
? "Add columns first before defining keys."
|
||||
: "No keys defined yet. Click \"Add Key\" to create primary or foreign keys."
|
||||
}
|
||||
</Typography>
|
||||
) : (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
{keys.map((key) => (
|
||||
<Paper key={key.id} variant="outlined" sx={{ p: 2 }}>
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid item xs={12} md={3}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Key Name"
|
||||
value={key.name}
|
||||
onChange={(e) => updateKey(key.id, 'name', e.target.value)}
|
||||
error={!!errors.keys?.[key.id]}
|
||||
helperText={errors.keys?.[key.id]}
|
||||
size="small"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={3}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>Column</InputLabel>
|
||||
<Select
|
||||
value={key.columnId}
|
||||
onChange={(e) => updateKey(key.id, 'columnId', e.target.value)}
|
||||
label="Column"
|
||||
>
|
||||
{columns.map(col => (
|
||||
<MenuItem key={col.id} value={col.id}>
|
||||
{col.name || 'Unnamed Column'}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>Key Type</InputLabel>
|
||||
<Select
|
||||
value={key.keyType}
|
||||
onChange={(e) => updateKey(key.id, 'keyType', e.target.value)}
|
||||
label="Key Type"
|
||||
>
|
||||
<MenuItem value="PRIMARY">Primary</MenuItem>
|
||||
<MenuItem value="FOREIGN">Foreign</MenuItem>
|
||||
<MenuItem value="UNIQUE">Unique</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Sequence"
|
||||
type="number"
|
||||
value={key.sequence}
|
||||
onChange={(e) => updateKey(key.id, 'sequence', parseInt(e.target.value) || 1)}
|
||||
size="small"
|
||||
inputProps={{ min: 1 }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
<IconButton
|
||||
onClick={() => removeKey(key.id)}
|
||||
color="error"
|
||||
size="small"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
|
||||
{/* Relations Section */}
|
||||
<Paper elevation={1} sx={{ p: 3 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||
<Typography variant="h6" color="primary">
|
||||
<LinkIcon style={{ marginRight: '8px', verticalAlign: 'middle' }} />
|
||||
Relations
|
||||
</Typography>
|
||||
<Button
|
||||
startIcon={<AddIcon />}
|
||||
onClick={addRelation}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
disabled={keys.length === 0 || existingTables.length === 0}
|
||||
>
|
||||
Add Relation
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{relations.length === 0 ? (
|
||||
<Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', py: 2 }}>
|
||||
{keys.length === 0
|
||||
? "Define keys first before creating relations."
|
||||
: existingTables.length === 0
|
||||
? "No existing tables available for relations."
|
||||
: "No relations defined yet. Click \"Add Relation\" to create table relationships."
|
||||
}
|
||||
</Typography>
|
||||
) : (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
{relations.map((relation) => (
|
||||
<Paper key={relation.id} variant="outlined" sx={{ p: 2 }}>
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid item xs={12} md={3}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>Target Table</InputLabel>
|
||||
<Select
|
||||
value={relation.targetTable}
|
||||
onChange={(e) => updateRelation(relation.id, 'targetTable', e.target.value)}
|
||||
label="Target Table"
|
||||
>
|
||||
{existingTables.map(table => (
|
||||
<MenuItem key={table.id} value={table.id}>
|
||||
{table.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>Source Key</InputLabel>
|
||||
<Select
|
||||
value={relation.sourceKey}
|
||||
onChange={(e) => updateRelation(relation.id, 'sourceKey', e.target.value)}
|
||||
label="Source Key"
|
||||
>
|
||||
{getAvailableKeys().map(key => (
|
||||
<MenuItem key={key.id} value={key.id}>
|
||||
{key.name} ({key.columnName})
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>Target Key</InputLabel>
|
||||
<Select
|
||||
value={relation.targetKey}
|
||||
onChange={(e) => updateRelation(relation.id, 'targetKey', e.target.value)}
|
||||
label="Target Key"
|
||||
disabled={!relation.targetTable}
|
||||
>
|
||||
{getTargetTableKeys(relation.targetTable).map(key => (
|
||||
<MenuItem key={key.id} value={key.id}>
|
||||
{key.name} ({key.type})
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>Relation Type</InputLabel>
|
||||
<Select
|
||||
value={relation.relationType}
|
||||
onChange={(e) => updateRelation(relation.id, 'relationType', e.target.value)}
|
||||
label="Relation Type"
|
||||
>
|
||||
<MenuItem value="1:1">One to One</MenuItem>
|
||||
<MenuItem value="1:N">One to Many</MenuItem>
|
||||
<MenuItem value="N:M">Many to Many</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={1}>
|
||||
<IconButton
|
||||
onClick={() => removeRelation(relation.id)}
|
||||
color="error"
|
||||
size="small"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions sx={{ p: 3, gap: 1 }}>
|
||||
<Button onClick={onClose} variant="outlined">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
variant="contained"
|
||||
disabled={isSubmitting}
|
||||
startIcon={isSubmitting ? null : <AddIcon />}
|
||||
>
|
||||
{isSubmitting ? 'Adding Table...' : 'Add Table'}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddTableModal;
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -18,9 +18,10 @@ import ReactFlow, {
|
|||
} from 'reactflow';
|
||||
import 'reactflow/dist/style.css';
|
||||
import axios from 'axios';
|
||||
import planPlusLogo from '../assets/img/planPlusLogo.png';
|
||||
|
||||
// Import icons from react-icons
|
||||
import { FaDatabase, FaTable, FaFlask, FaArrowRight, FaPlus, FaTimes } from 'react-icons/fa';
|
||||
import { FaDatabase, FaTable, FaFlask, FaArrowRight, FaPlus, FaTimes, FaChevronUp, FaChevronDown } from 'react-icons/fa';
|
||||
import { BiSolidData } from 'react-icons/bi';
|
||||
import { AiFillFolder } from 'react-icons/ai';
|
||||
import { BsFileEarmarkSpreadsheet } from 'react-icons/bs';
|
||||
|
|
@ -944,7 +945,197 @@ const CustomEdge = ({ id, source, target, sourceX, sourceY, targetX, targetY, so
|
|||
);
|
||||
};
|
||||
|
||||
// Create a unique ID for edges that should be animated
|
||||
const SERVICE_EDGE_CLASS = 'service-db-connection';
|
||||
|
||||
// Add the CSS for the animated connections directly to the stylesheet
|
||||
// This avoids potential issues with React's rendering cycle
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes dashdraw {
|
||||
from {
|
||||
stroke-dashoffset: 10;
|
||||
}
|
||||
to {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
.${SERVICE_EDGE_CLASS} .react-flow__edge-path {
|
||||
animation: dashdraw 0.5s linear infinite;
|
||||
}
|
||||
`;
|
||||
// Add the style element only once
|
||||
if (!document.getElementById('react-flow-animation')) {
|
||||
style.id = 'react-flow-animation';
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
// Hierarchical Edge for connecting service to databases
|
||||
const HierarchicalEdge = ({ id, source, target, sourceX, sourceY, targetX, targetY, style = {} }) => {
|
||||
// Calculate the path points for a stepped edge with orthogonal lines
|
||||
const [path] = getSmoothStepPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition: Position.Bottom, // Always start from bottom
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition: Position.Top, // Always connect to top
|
||||
borderRadius: 20, // Smoother corners for longer connections
|
||||
stepSize: 30, // Increased step size for longer connections
|
||||
});
|
||||
|
||||
// Enhanced style for hierarchical connections with animated dotted line
|
||||
const hierarchyStyle = {
|
||||
strokeWidth: 2,
|
||||
stroke: '#00a99d',
|
||||
strokeDasharray: '5, 5', // Create dotted/dashed line effect
|
||||
...style,
|
||||
};
|
||||
|
||||
return (
|
||||
<g className={SERVICE_EDGE_CLASS}>
|
||||
<BaseEdge
|
||||
id={id}
|
||||
path={path}
|
||||
style={hierarchyStyle}
|
||||
markerEnd={{
|
||||
type: 'arrowclosed',
|
||||
width: 16,
|
||||
height: 16,
|
||||
color: '#00a99d',
|
||||
strokeWidth: 2,
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
// Custom node types
|
||||
const ServiceNode = ({ data = {} }) => {
|
||||
// Safety check for undefined data
|
||||
if (!data) {
|
||||
console.error('ServiceNode received undefined data');
|
||||
data = {}; // Provide a default empty object
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: '20px',
|
||||
borderRadius: '12px',
|
||||
background: 'linear-gradient(145deg, #222, #2a2a2a)',
|
||||
boxShadow: '0 10px 30px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(0, 169, 157, 0.3) inset',
|
||||
border: '2px solid #00a99d',
|
||||
color: '#fff',
|
||||
width: '240px',
|
||||
minHeight: '110px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'relative',
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
justifyContent: 'space-between'
|
||||
}}>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
marginBottom: '16px'
|
||||
}}>
|
||||
{/* Logo and text container */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginBottom: '8px'
|
||||
}}>
|
||||
<img
|
||||
src={planPlusLogo}
|
||||
alt="Logo"
|
||||
width="85"
|
||||
style={{
|
||||
marginRight: '2px',
|
||||
verticalAlign: 'middle'
|
||||
}}
|
||||
/>
|
||||
<span style={{
|
||||
fontWeight: '600',
|
||||
fontSize: '20px',
|
||||
color: '#fff',
|
||||
marginLeft: '2px',
|
||||
marginTop: '2px'
|
||||
}}>
|
||||
Plan+
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Connected databases count */}
|
||||
<div style={{
|
||||
fontSize: '12px',
|
||||
color: '#aaa',
|
||||
marginTop: '2px',
|
||||
textAlign: 'center'
|
||||
}}>
|
||||
Connected Databases: {data.databases || 0}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: '8px'
|
||||
}}>
|
||||
<button
|
||||
style={{
|
||||
background: 'linear-gradient(145deg, rgba(0, 169, 157, 0.2), rgba(0, 169, 157, 0.1))',
|
||||
border: '1px solid rgba(0, 169, 157, 0.4)',
|
||||
borderRadius: '6px',
|
||||
padding: '8px 16px',
|
||||
color: '#00a99d',
|
||||
fontSize: '12px',
|
||||
fontWeight: '500',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px',
|
||||
boxShadow: '0 2px 5px rgba(0, 0, 0, 0.2)',
|
||||
transition: 'all 0.2s ease',
|
||||
width: '100%',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
onClick={() => data.onToggle && data.onToggle(data.id)}
|
||||
>
|
||||
{data.expanded ? (
|
||||
<>
|
||||
<span>Collapse Connections</span>
|
||||
<FaChevronUp size={10} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span>Expand Connections</span>
|
||||
<FaChevronDown size={10} />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Handles for connections - explicit id for targeting */}
|
||||
<Handle
|
||||
id="bottom"
|
||||
type="source"
|
||||
position={Position.Bottom}
|
||||
style={{
|
||||
background: '#00a99d',
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
border: '2px solid #00a99d',
|
||||
boxShadow: '0 0 5px rgba(0, 169, 157, 0.5)',
|
||||
bottom: '-5px' // Move it slightly outside the node for better connection
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DatabaseNode = ({ data = {} }) => {
|
||||
// Safety check for undefined data
|
||||
if (!data) {
|
||||
|
|
@ -952,6 +1143,8 @@ const DatabaseNode = ({ data = {} }) => {
|
|||
data = {}; // Provide a default empty object
|
||||
}
|
||||
|
||||
// Enhanced styles for handles used in hierarchy connections
|
||||
|
||||
// Debug: Log the data being received by the component
|
||||
// console.log('DatabaseNode data:', data);
|
||||
|
||||
|
|
@ -1878,6 +2071,7 @@ const InfiniteCanvas = () => {
|
|||
|
||||
// Define node types (memoized to prevent unnecessary re-renders)
|
||||
const nodeTypes = useMemo(() => ({
|
||||
service: ServiceNode,
|
||||
database: DatabaseNode,
|
||||
schema: SchemaNode,
|
||||
table: TableNode,
|
||||
|
|
@ -1886,6 +2080,7 @@ const InfiniteCanvas = () => {
|
|||
// Define edge types
|
||||
const edgeTypes = useMemo(() => ({
|
||||
custom: CustomEdge,
|
||||
hierarchical: HierarchicalEdge,
|
||||
}), []);
|
||||
|
||||
// Function to handle redirection to DataFlow view
|
||||
|
|
@ -1968,8 +2163,30 @@ const InfiniteCanvas = () => {
|
|||
// No alert needed for MVP - the view will change automatically
|
||||
};
|
||||
|
||||
// Initialize with database nodes from state instead of mockData
|
||||
const initialNodes = databases
|
||||
// Create a service node for "Plan Plus"
|
||||
const serviceNode = {
|
||||
id: 'service-plan-plus',
|
||||
type: 'service',
|
||||
data: {
|
||||
id: 'service-plan-plus',
|
||||
// label: 'Qubit Service: Plan+',
|
||||
databases: databases.length,
|
||||
expanded: true,
|
||||
onToggle: (id) => {
|
||||
// Toggle expansion of the service node
|
||||
setNodes(nodes => nodes.map(node => {
|
||||
if (node.id === id) {
|
||||
return { ...node, data: { ...node.data, expanded: !node.data.expanded } };
|
||||
}
|
||||
return node;
|
||||
}));
|
||||
}
|
||||
},
|
||||
position: { x: 400, y: 70 } // Positioned even higher on the canvas for maximum spacing
|
||||
};
|
||||
|
||||
// Initialize with database nodes as children of the service
|
||||
const databaseNodes = databases
|
||||
.filter(db => db && db.id) // Filter out any invalid database objects
|
||||
.map((db, index) => {
|
||||
// Create a safe ID for the database
|
||||
|
|
@ -1988,15 +2205,36 @@ const InfiniteCanvas = () => {
|
|||
onToggle: (id) => toggleDatabaseExpansion(id),
|
||||
onViewDetails: handleViewDataFlow // Add the function to handle redirection
|
||||
},
|
||||
// Position databases in a grid layout for better visibility
|
||||
// Position databases in a grid layout with significantly increased vertical spacing
|
||||
position: {
|
||||
x: 250 + (index % 2) * 400, // 2 columns
|
||||
y: 100 + Math.floor(index / 2) * 250 // rows based on index
|
||||
y: 400 + Math.floor(index / 2) * 250 // further increased vertical spacing (400px) from service node
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const initialEdges = [];
|
||||
// Combine the service node with database nodes
|
||||
const initialNodes = [
|
||||
serviceNode,
|
||||
...databaseNodes
|
||||
];
|
||||
|
||||
// Create edges from service node to each database with custom hierarchical connections
|
||||
const initialEdges = databases
|
||||
.filter(db => db && db.id)
|
||||
.map((db) => ({
|
||||
id: `edge-service-to-${db.id}`,
|
||||
source: 'service-plan-plus',
|
||||
target: db.id,
|
||||
type: 'hierarchical', // Using our custom hierarchical edge
|
||||
animated: true,
|
||||
style: {
|
||||
stroke: '#00a99d',
|
||||
strokeWidth: 2,
|
||||
strokeDasharray: '5, 5', // Create dotted/dashed line effect
|
||||
},
|
||||
// No need for handles as they're built into the edge type
|
||||
}));
|
||||
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||
|
|
@ -2005,7 +2243,30 @@ const InfiniteCanvas = () => {
|
|||
useEffect(() => {
|
||||
console.log('Databases updated, updating nodes:', databases);
|
||||
|
||||
const updatedNodes = databases
|
||||
// Create a parent service node for "Plan Plus"
|
||||
const serviceNode = {
|
||||
id: 'service-plan-plus',
|
||||
type: 'service',
|
||||
data: {
|
||||
id: 'service-plan-plus',
|
||||
// label: 'Qubit Service: Plan+',
|
||||
databases: databases.length,
|
||||
expanded: true,
|
||||
onToggle: (id) => {
|
||||
// Toggle expansion of the service node
|
||||
setNodes(nodes => nodes.map(node => {
|
||||
if (node.id === id) {
|
||||
return { ...node, data: { ...node.data, expanded: !node.data.expanded } };
|
||||
}
|
||||
return node;
|
||||
}));
|
||||
}
|
||||
},
|
||||
position: { x: 400, y: 70 } // Positioned even higher on the canvas for maximum spacing
|
||||
};
|
||||
|
||||
// Map database nodes as children of the service node
|
||||
const databaseNodes = databases
|
||||
.filter(db => db && db.id)
|
||||
.map((db, index) => {
|
||||
const existingNode = nodes.find(node => node.id === db.id);
|
||||
|
|
@ -2020,6 +2281,11 @@ const InfiniteCanvas = () => {
|
|||
name: db.name, // Make sure name is set from the API response
|
||||
schemas: db.schemas || 0,
|
||||
tables: db.tables || 0,
|
||||
},
|
||||
// Update position with significantly increased vertical spacing
|
||||
position: {
|
||||
x: 250 + (index % 2) * 400, // 2 columns
|
||||
y: 400 + Math.floor(index / 2) * 250 // further increased vertical spacing (400px) from service node
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -2038,16 +2304,41 @@ const InfiniteCanvas = () => {
|
|||
onToggle: (id) => toggleDatabaseExpansion(id),
|
||||
onViewDetails: handleViewDataFlow
|
||||
},
|
||||
// Position databases in a grid layout with significantly increased vertical spacing
|
||||
position: {
|
||||
x: 250 + (index % 2) * 400,
|
||||
y: 100 + Math.floor(index / 2) * 250
|
||||
y: 400 + Math.floor(index / 2) * 250 // further increased vertical spacing (400px) from service node
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Create edges from service node to each database with custom hierarchical connections
|
||||
const serviceEdges = databases
|
||||
.filter(db => db && db.id)
|
||||
.map((db) => ({
|
||||
id: `edge-service-to-${db.id}`,
|
||||
source: 'service-plan-plus',
|
||||
target: db.id,
|
||||
type: 'hierarchical', // Using our custom hierarchical edge
|
||||
animated: true,
|
||||
style: {
|
||||
stroke: '#00a99d',
|
||||
strokeWidth: 2,
|
||||
strokeDasharray: '5, 5', // Create dotted/dashed line effect
|
||||
},
|
||||
// No need for handles as they're built into the edge type
|
||||
}));
|
||||
|
||||
// Combine service node with database nodes
|
||||
const updatedNodes = [
|
||||
serviceNode,
|
||||
...databaseNodes
|
||||
];
|
||||
|
||||
// Only update if we have nodes to show
|
||||
if (updatedNodes.length > 0) {
|
||||
setNodes(updatedNodes);
|
||||
setEdges(serviceEdges);
|
||||
}
|
||||
}, [databases]);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue