From 3de53aeabf8effe8895c8022b43acd2828bb24e5 Mon Sep 17 00:00:00 2001 From: Geoffrey Yu Date: Thu, 23 Jan 2025 18:29:04 -0500 Subject: [PATCH] Dashboard UI element enhancements (#516) --- ui/package-lock.json | 316 +++++++++--------- ui/package.json | 3 +- ui/src/App.css | 43 ++- ui/src/App.jsx | 42 ++- ui/src/components/BlueprintView.jsx | 8 +- ui/src/components/CreateEditVdbeForm.jsx | 219 ++++++++++++ ui/src/components/ExpandableTableSet.jsx | 47 +++ ui/src/components/InsetPanel.jsx | 7 + ui/src/components/PerfView.jsx | 6 +- ui/src/components/PhysDbView.jsx | 67 ++-- ui/src/components/VdbeView.jsx | 113 +++++-- ui/src/components/VirtualInfraView.jsx | 30 +- ui/src/components/WorkloadInput.jsx | 60 ++++ ui/src/components/styles/BlueprintView.css | 2 +- .../components/styles/CreateEditVdbeForm.css | 42 +++ .../components/styles/ExpandableTableSet.css | 91 +++++ ui/src/components/styles/InsetPanel.css | 15 + ui/src/components/styles/PerfView.css | 2 + ui/src/components/styles/TableView.css | 9 - ui/src/components/styles/VdbeView.css | 47 +++ ui/src/components/styles/VirtualInfraView.css | 10 +- ui/src/components/styles/WorkloadInput.css | 26 ++ ui/src/index.css | 68 ---- ui/src/main.jsx | 1 - 24 files changed, 929 insertions(+), 345 deletions(-) create mode 100644 ui/src/components/CreateEditVdbeForm.jsx create mode 100644 ui/src/components/ExpandableTableSet.jsx create mode 100644 ui/src/components/InsetPanel.jsx create mode 100644 ui/src/components/WorkloadInput.jsx create mode 100644 ui/src/components/styles/CreateEditVdbeForm.css create mode 100644 ui/src/components/styles/ExpandableTableSet.css create mode 100644 ui/src/components/styles/InsetPanel.css create mode 100644 ui/src/components/styles/WorkloadInput.css delete mode 100644 ui/src/index.css diff --git a/ui/package-lock.json b/ui/package-lock.json index afded796..b349cf22 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", - "@mui/material": "^5.15.14", + "@mui/icons-material": "^6.4.1", + "@mui/material": "^6.4.1", "axios": "^1.6.7", "chart.js": "^4.4.2", "react": "^18.2.0", @@ -325,9 +326,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -418,21 +419,31 @@ } }, "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "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==", + "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/cache/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==" + }, + "node_modules/@emotion/cache/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==" + }, "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==" }, "node_modules/@emotion/is-prop-valid": { "version": "1.2.2", @@ -471,21 +482,26 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", - "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", - "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "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/serialize/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==" + }, "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" }, "node_modules/@emotion/styled": { "version": "11.11.0", @@ -510,9 +526,9 @@ } }, "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { "version": "1.0.1", @@ -523,9 +539,9 @@ } }, "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" }, "node_modules/@emotion/weak-memoize": { "version": "0.3.1", @@ -971,40 +987,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@floating-ui/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", - "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", - "dependencies": { - "@floating-ui/utils": "^0.2.1" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", - "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", - "dependencies": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.0" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", - "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", - "dependencies": { - "@floating-ui/dom": "^1.6.1" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -1091,30 +1073,33 @@ "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.40", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", - "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", + "node_modules/@mui/core-downloads-tracker": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.1.tgz", + "integrity": "sha512-SfDLWMV5b5oXgDf3NTa2hCTPC1d2defhDH2WgFKmAiejC4mSfXYbyi+AFCLzpizauXhgBm8OaZy9BHKnrSpahQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.1.tgz", + "integrity": "sha512-wsxFcUTQxt4s+7Bg4GgobqRjyaHLmZGNOs+HJpbwrwmLbT6mhIJxhpqsKzzWq9aDY8xIe7HCjhpH7XI5UD6teA==", "dependencies": { - "@babel/runtime": "^7.23.9", - "@floating-ui/react-dom": "^2.0.8", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", - "@popperjs/core": "^2.11.8", - "clsx": "^2.1.0", - "prop-types": "^15.8.1" + "@babel/runtime": "^7.26.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^6.4.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1122,35 +1107,26 @@ } } }, - "node_modules/@mui/core-downloads-tracker": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.14.tgz", - "integrity": "sha512-on75VMd0XqZfaQW+9pGjSNiqW+ghc5E2ZSLRBXwcXl/C4YzjfyjrLPhrEpKnR9Uym9KXBvxrhoHfPcczYHweyA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - } - }, "node_modules/@mui/material": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.14.tgz", - "integrity": "sha512-kEbRw6fASdQ1SQ7LVdWR5OlWV3y7Y54ZxkLzd6LV5tmz+NpO3MJKZXSfgR0LHMP7meKsPiMm4AuzV0pXDpk/BQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/base": "5.0.0-beta.40", - "@mui/core-downloads-tracker": "^5.15.14", - "@mui/system": "^5.15.14", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", - "@types/react-transition-group": "^4.4.10", - "clsx": "^2.1.0", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.1.tgz", + "integrity": "sha512-MFBfia6UiKxyoLeGkAh8M15bkeDmfnsUTMRJd/vTQue6YQ8AQ6lw9HqDthyYghzDEWIvZO/lQQzLrZE8XwNJLA==", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/core-downloads-tracker": "^6.4.1", + "@mui/system": "^6.4.1", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.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": "^18.2.0", + "react-is": "^19.0.0", "react-transition-group": "^4.4.5" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", @@ -1159,9 +1135,10 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material-pigment-css": "^6.4.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": { @@ -1170,35 +1147,38 @@ "@emotion/styled": { "optional": true }, + "@mui/material-pigment-css": { + "optional": true + }, "@types/react": { "optional": true } } }, "node_modules/@mui/material/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==" }, "node_modules/@mui/private-theming": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", - "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.1.tgz", + "integrity": "sha512-DcT7mwK89owwgcEuiE7w458te4CIjHbYWW6Kn6PiR6eLtxBsoBYphA968uqsQAOBQDpbYxvkuFLwhgk4bxoN/Q==", "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.15.14", + "@babel/runtime": "^7.26.0", + "@mui/utils": "^6.4.1", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1207,17 +1187,19 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", - "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@emotion/cache": "^11.11.0", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.0.tgz", + "integrity": "sha512-ek/ZrDujrger12P6o4luQIfRd2IziH7jQod2WMbLqGE03Iy0zUwYmckRTVhRQTLPNccpD8KXGcALJF+uaUQlbg==", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@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": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", @@ -1226,7 +1208,7 @@ "peerDependencies": { "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -1238,21 +1220,21 @@ } }, "node_modules/@mui/system": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.14.tgz", - "integrity": "sha512-auXLXzUaCSSOLqJXmsAaq7P96VPRXg2Rrz6OHNV7lr+kB8lobUF+/N84Vd9C4G/wvCXYPs5TYuuGBRhcGbiBGg==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.14", - "@mui/styled-engine": "^5.15.14", - "@mui/types": "^7.2.14", - "@mui/utils": "^5.15.14", - "clsx": "^2.1.0", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.1.tgz", + "integrity": "sha512-rgQzgcsHCTtzF9MZ+sL0tOhf2ZBLazpjrujClcb4Siju5lTrK0xX4PsiropActzCemNfM+mOu+0jezAVnfRK8g==", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/private-theming": "^6.4.1", + "@mui/styled-engine": "^6.4.0", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.1", + "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", @@ -1261,8 +1243,8 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.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": { @@ -1277,11 +1259,11 @@ } }, "node_modules/@mui/types": { - "version": "7.2.14", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", - "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", + "version": "7.2.21", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz", + "integrity": "sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==", "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1290,25 +1272,27 @@ } }, "node_modules/@mui/utils": { - "version": "5.15.14", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", - "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@types/prop-types": "^15.7.11", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.1.tgz", + "integrity": "sha512-iQUDUeYh87SvR4lVojaRaYnQix8BbRV51MxaV6MBmqthecQoxwSbS5e2wnbDJUeFxY2ppV505CiqPLtd0OWkqw==", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.21", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "react-is": "^19.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1317,9 +1301,9 @@ } }, "node_modules/@mui/utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -1587,9 +1571,9 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" }, "node_modules/@types/react": { "version": "18.2.62", @@ -1611,10 +1595,10 @@ } }, "node_modules/@types/react-transition-group": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", - "dependencies": { + "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==", + "peerDependencies": { "@types/react": "*" } }, @@ -2037,9 +2021,9 @@ } }, "node_modules/clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "engines": { "node": ">=6" } diff --git a/ui/package.json b/ui/package.json index d54c71cc..3f719dce 100644 --- a/ui/package.json +++ b/ui/package.json @@ -16,7 +16,8 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", - "@mui/material": "^5.15.14", + "@mui/icons-material": "^6.4.1", + "@mui/material": "^6.4.1", "axios": "^1.6.7", "chart.js": "^4.4.2", "react": "^18.2.0", diff --git a/ui/src/App.css b/ui/src/App.css index 14626ef6..cc798c6f 100644 --- a/ui/src/App.css +++ b/ui/src/App.css @@ -1,26 +1,20 @@ +:root { + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + html, body { - height: 100%; - margin: 0; - padding: 0; - font-family: "Source Sans 3", sans-serif; font-optical-sizing: auto; font-weight: 400; font-style: normal; } -body { - justify-content: center !important; - align-items: flex-start !important; -} - #root { - width: 100%; - height: auto; - min-height: 100%; background-color: #fff; - display: flex; justify-content: center; align-items: stretch; @@ -50,6 +44,8 @@ body { margin: 0; font-size: 1.75em; font-weight: 600; + display: flex; + align-items: center; } .column-inner { @@ -72,6 +68,27 @@ body { flex-grow: 1; } +.infra-separator { + box-shadow: 0px 20px 20px -15px rgba(0, 0, 0, 0.1); + height: 40px; + width: 95%; + margin: 20px auto 40px auto; +} + +.infra-region { + position: relative; +} + +.infra-region h2 { + position: absolute; + top: 0; + left: 0; + font-size: 1.75em; + color: #ababab; + transform-origin: top left; + transform: rotate(-90deg) translate(calc(-100% - 20px), -10px); +} + /* Responsive layout */ @media only screen and (max-width: 800px) { .body-container { diff --git a/ui/src/App.jsx b/ui/src/App.jsx index 4fa17b16..7e7cc68d 100644 --- a/ui/src/App.jsx +++ b/ui/src/App.jsx @@ -3,6 +3,10 @@ import Header from "./components/Header"; import VirtualInfraView from "./components/VirtualInfraView"; import BlueprintView from "./components/BlueprintView"; import PerfView from "./components/PerfView"; +import WorkloadInput from "./components/WorkloadInput"; +import CreateEditVdbeForm from "./components/CreateEditVdbeForm"; +import StorageRoundedIcon from "@mui/icons-material/StorageRounded"; +import Panel from "./components/Panel"; import SystemConfig from "./components/SystemConfig"; import { fetchSystemState } from "./api"; @@ -117,22 +121,30 @@ function App() {
-

Data Infrastructure

+

+ + Data Infrastructure +

- - + + + + +
+ +
diff --git a/ui/src/components/BlueprintView.jsx b/ui/src/components/BlueprintView.jsx index 12146cfd..612bdfa6 100644 --- a/ui/src/components/BlueprintView.jsx +++ b/ui/src/components/BlueprintView.jsx @@ -1,4 +1,3 @@ -import Panel from "./Panel"; import PhysDbView from "./PhysDbView"; import "./styles/BlueprintView.css"; @@ -20,8 +19,9 @@ function BlueprintView({ onTableHoverExit, }) { return ( - -
+
+

Physical

+
{blueprint && blueprint.engines && blueprint.engines.map(({ name, ...props }) => ( @@ -36,7 +36,7 @@ function BlueprintView({ /> ))}
- +
); } diff --git a/ui/src/components/CreateEditVdbeForm.jsx b/ui/src/components/CreateEditVdbeForm.jsx new file mode 100644 index 00000000..da53ba55 --- /dev/null +++ b/ui/src/components/CreateEditVdbeForm.jsx @@ -0,0 +1,219 @@ +import { useState } from "react"; +import { useTheme } from "@mui/material/styles"; +import InsetPanel from "./InsetPanel"; +import CheckCircleOutlineRoundedIcon from "@mui/icons-material/CheckCircleOutlineRounded"; +import FormControl from "@mui/material/FormControl"; +import InputLabel from "@mui/material/InputLabel"; +import TextField from "@mui/material/TextField"; +import MenuItem from "@mui/material/MenuItem"; +import InputAdornment from "@mui/material/InputAdornment"; +import Button from "@mui/material/Button"; +import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; +import EditRoundedIcon from "@mui/icons-material/EditRounded"; +import Select from "@mui/material/Select"; +import Chip from "@mui/material/Chip"; +import Box from "@mui/material/Box"; +import OutlinedInput from "@mui/material/OutlinedInput"; +import VdbeView from "./VdbeView"; +import "./styles/CreateEditVdbeForm.css"; + +const ITEM_HEIGHT = 47; +const ITEM_PADDING_TOP = 7; +const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 3.5 + ITEM_PADDING_TOP, + width: 249, + }, + }, +}; + +function getStyles(name, selectedTables, theme) { + return { + fontWeight: selectedTables.includes(name) + ? theme.typography.fontWeightMedium + : theme.typography.fontWeightRegular, + }; +} + +function TableSelector({ tables }) { + const theme = useTheme(); + const [selectedTables, setSelectedTables] = useState([]); + + const handleChange = (event) => { + const { + target: { value }, + } = event; + setSelectedTables( + // On autofill we get a stringified value. + typeof value === "string" ? value.split(",") : value, + ); + }; + + return ( +
+ + Tables + + +
+ ); +} + +function CreateEditFormFields({ vdbe, setVdbe }) { + const onStalenessChange = (event) => { + const maxStalenessMins = parseInt(event.target.value); + if (isNaN(maxStalenessMins)) { + setVdbe({ ...vdbe, max_staleness_ms: null }); + return; + } + setVdbe({ ...vdbe, max_staleness_ms: maxStalenessMins * 60 * 1000 }); + }; + + const onSloChange = (event) => { + const sloMs = parseInt(event.target.value); + if (isNaN(sloMs)) { + setVdbe({ ...vdbe, p90_latency_slo_ms: null }); + return; + } + setVdbe({ ...vdbe, p90_latency_slo_ms: sloMs }); + }; + + const tables = ["tickets", "theatres", "movies"]; + + return ( +
+ setVdbe({ ...vdbe, name: event.target.value })} + /> + minutes + ), + }, + }} + value={ + vdbe.max_staleness_ms != null ? vdbe.max_staleness_ms / 60000 : "" + } + onChange={onStalenessChange} + /> + milliseconds + ), + }, + }} + value={vdbe.p90_latency_slo_ms != null ? vdbe.p90_latency_slo_ms : ""} + onChange={onSloChange} + /> + + Query Interface + + + +
+ ); +} + +function getEmptyVdbe() { + return { + name: null, + max_staleness_ms: null, + p90_latency_slo_ms: null, + queryInterface: "postgresql", + tables: [], + }; +} + +function CreateEditVdbeForm({ isEdit, currentVdbe }) { + const [vdbe, setVdbe] = useState( + currentVdbe != null ? currentVdbe : getEmptyVdbe(), + ); + + return ( + +

+ {isEdit ? ( + + ) : ( + + )} + {isEdit ? "Edit VDBE" : "Create VDBE"} +

+
+ +
+ +
+
+
+ + +
+
+ ); +} + +export default CreateEditVdbeForm; diff --git a/ui/src/components/ExpandableTableSet.jsx b/ui/src/components/ExpandableTableSet.jsx new file mode 100644 index 00000000..35fb0ee1 --- /dev/null +++ b/ui/src/components/ExpandableTableSet.jsx @@ -0,0 +1,47 @@ +import { useState } from "react"; +import ArrowDropDownRoundedIcon from "@mui/icons-material/ArrowDropDownRounded"; +import ArrowDropUpRoundedIcon from "@mui/icons-material/ArrowDropUpRounded"; +import "./styles/ExpandableTableSet.css"; + +function ShowMore({ onClick, total }) { + return ( +
+ + Show More ({total - 6}) + +
+ ); +} + +function ShowLess({ onClick }) { + return ( +
+ + Show Less + +
+ ); +} + +function ExpandableTableSet({ children }) { + const needsExpansion = children.length > 6; + const [expanded, setExpanded] = useState(false); + + return ( +
+
+
{children}
+
+ {needsExpansion && !expanded && ( + setExpanded(true)} total={children.length} /> + )} + {needsExpansion && expanded && ( + setExpanded(false)} /> + )} +
+ ); +} + +export default ExpandableTableSet; diff --git a/ui/src/components/InsetPanel.jsx b/ui/src/components/InsetPanel.jsx new file mode 100644 index 00000000..78c183de --- /dev/null +++ b/ui/src/components/InsetPanel.jsx @@ -0,0 +1,7 @@ +import "./styles/InsetPanel.css"; + +function InsetPanel({ children, className }) { + return
{children}
; +} + +export default InsetPanel; diff --git a/ui/src/components/PerfView.jsx b/ui/src/components/PerfView.jsx index 62aa2d1d..d10c794f 100644 --- a/ui/src/components/PerfView.jsx +++ b/ui/src/components/PerfView.jsx @@ -3,6 +3,7 @@ import { fetchMetrics } from "../api"; import MetricsManager from "../metrics"; import Panel from "./Panel"; import LatencyPlot from "./LatencyPlot"; +import TroubleshootRoundedIcon from "@mui/icons-material/TroubleshootRounded"; import "./styles/PerfView.css"; const REFRESH_INTERVAL_MS = 30 * 1000; @@ -134,7 +135,10 @@ function PerfView({ virtualInfra }) { return (
-

Performance Monitoring

+

+ + Performance Monitoring +

( + + onTableHoverEnter(physDbName, name, false, mapped_to) + } + onTableHoverExit={onTableHoverExit} + /> + ), + ); + const addedTableComponents = addedTablesList.map(({ name, writable }) => ( + {}} + onTableHoverExit={() => {}} + /> + )); + return (
)} -
- {sortedTables.map(({ name, writable, mapped_to }) => ( - - onTableHoverEnter(physDbName, name, false, mapped_to) - } - onTableHoverExit={onTableHoverExit} - /> - ))} - {addedTablesList.map(({ name, writable }) => ( - {}} - onTableHoverExit={() => {}} - /> - ))} -
+ + {[...sortedTableComponents, ...addedTableComponents]} +
); } diff --git a/ui/src/components/VdbeView.jsx b/ui/src/components/VdbeView.jsx index 3f23ea20..2e562288 100644 --- a/ui/src/components/VdbeView.jsx +++ b/ui/src/components/VdbeView.jsx @@ -1,15 +1,25 @@ +import { useState } from "react"; import DbCylinder from "./DbCylinder"; import TableView from "./TableView"; -import WorkloadAdjuster from "./WorkloadAdjuster"; +import ExpandableTableSet from "./ExpandableTableSet"; +import IconButton from "@mui/material/IconButton"; +import Tooltip from "@mui/material/Tooltip"; +import EditRoundedIcon from "@mui/icons-material/EditRounded"; +import DeleteRoundedIcon from "@mui/icons-material/DeleteRounded"; +import LinkRoundedIcon from "@mui/icons-material/LinkRounded"; +import Snackbar from "@mui/material/Snackbar"; import "./styles/VdbeView.css"; import { highlightTableViewClass, highlightEngineViewClass, sortTablesToHoist, } from "../highlight"; -import { useState, useCallback } from "react"; function formatMilliseconds(milliseconds) { + if (milliseconds == null) { + return null; + } + const precision = 2; if (milliseconds >= 1000 * 60 * 60) { // Use hours. @@ -24,6 +34,10 @@ function formatMilliseconds(milliseconds) { } function formatFreshness(maxStalenessMs) { + if (maxStalenessMs == null) { + return null; + } + if (maxStalenessMs === 0) { return "No staleness"; } @@ -31,60 +45,103 @@ function formatFreshness(maxStalenessMs) { } function formatDialect(queryInterface) { + if (queryInterface == null) { + return null; + } + if (queryInterface === "postgresql") { return "PostgreSQL SQL"; } else if (queryInterface === "athena") { return "Athena SQL"; } else if (queryInterface === "common") { return "SQL-99"; + } else { + console.error("Unknown", queryInterface); + return null; } } +function EditControls({ onEditClick, onDeleteClick }) { + return ( +
+ + + + + + + + + + +
+ ); +} + +function VdbeEndpoint({ endpoint, setShowSnackbar }) { + const handleCopy = () => { + navigator.clipboard.writeText(endpoint); + setShowSnackbar(true); + }; + return ( +
+ + + {endpoint} + +
+ ); +} + function VdbeView({ vdbe, + endpoint, highlight, onTableHoverEnter, onTableHoverExit, - workloadState, - updateWorkloadNumClients, + editable, }) { const vengName = vdbe.name; const tables = vdbe.tables; const freshness = formatFreshness(vdbe.max_staleness_ms); const peakLatency = formatMilliseconds(vdbe.p90_latency_slo_ms); const dialect = formatDialect(vdbe.interface); - const sortedTables = sortTablesToHoist(highlight, vengName, true, tables); + const [showSnackbar, setShowSnackbar] = useState(false); - const [showWorkloadAdjuster, setShowWorkloadAdjuster] = useState(false); - const toggleWorkloadAdjuster = useCallback(() => { - setShowWorkloadAdjuster(!showWorkloadAdjuster); - }, [showWorkloadAdjuster]); + const handleClose = (event, reason) => { + if (reason === "clickaway") { + return; + } + setShowSnackbar(false); + }; return (
- {workloadState && showWorkloadAdjuster && ( - +
+ {vengName} + {editable && ( + {}} onDeleteClick={() => {}} /> + )} +
+ {endpoint && ( + )} - - {vengName} -
    -
  • 🌿: {freshness}
  • -
  • ⏱️: p90 Query Latency ≤ {peakLatency}
  • -
  • 🗣: {dialect}
  • +
  • 🌿: {freshness != null ? freshness : "-----"}
  • +
  • + ⏱️:{" "} + {peakLatency != null + ? `p90 Query Latency ≤ ${peakLatency}` + : "-----"} +
  • +
  • 🗣: {dialect != null ? dialect : "-----"}
-
+ {sortedTables.map(({ name, writable, mapped_to }) => ( ))} -
+ +
); } diff --git a/ui/src/components/VirtualInfraView.jsx b/ui/src/components/VirtualInfraView.jsx index 27960315..974d8442 100644 --- a/ui/src/components/VirtualInfraView.jsx +++ b/ui/src/components/VirtualInfraView.jsx @@ -1,5 +1,6 @@ -import Panel from "./Panel"; import VdbeView from "./VdbeView"; +import AddCircleOutlineRoundedIcon from "@mui/icons-material/AddCircleOutlineRounded"; +import Button from "@mui/material/Button"; import "./styles/VirtualInfraView.css"; import { useEffect, useState, useCallback } from "react"; import { fetchWorkloadClients, setWorkloadClients } from "../api"; @@ -58,23 +59,34 @@ function VirtualInfraView({ }, [endpoints]); return ( - -
- {virtualInfra?.engines?.map((vdbe, index) => ( +
+

Virtual

+
+ {virtualInfra?.engines?.map((vdbe) => ( - updateWorkloadNumClients(index, numClients) - } vdbe={vdbe} + editable={true} /> ))}
- +
+ +
+
); } diff --git a/ui/src/components/WorkloadInput.jsx b/ui/src/components/WorkloadInput.jsx new file mode 100644 index 00000000..aa256fb3 --- /dev/null +++ b/ui/src/components/WorkloadInput.jsx @@ -0,0 +1,60 @@ +import Button from "@mui/material/Button"; +import Slider from "@mui/material/Slider"; +import InsetPanel from "./InsetPanel"; +import AutoFixHighRoundedIcon from "@mui/icons-material/AutoFixHighRounded"; +import TuneRoundedIcon from "@mui/icons-material/TuneRounded"; +import HelpRoundedIcon from "@mui/icons-material/HelpRounded"; +import Tooltip from "@mui/material/Tooltip"; +import "./styles/WorkloadInput.css"; + +function WorkloadSlider({ engineName, min, max, value, setValue }) { + return ( +
+

Clients on {engineName}

+ setValue(newValue)} + /> +
+ ); +} + +function WorkloadInput({ min, max }) { + const instructions = + "Use the sliders to change the number of clients accessing each VDBE. " + + "Then, click 'Show Predicted Changes' to see BRAD's predictions for how " + + "the physical infrastructure will change."; + return ( + +

+ + Adjust Workload + + + +

+
+ + + +
+
+ + +
+
+ ); +} + +export default WorkloadInput; diff --git a/ui/src/components/styles/BlueprintView.css b/ui/src/components/styles/BlueprintView.css index 75053416..d2f4537e 100644 --- a/ui/src/components/styles/BlueprintView.css +++ b/ui/src/components/styles/BlueprintView.css @@ -1,4 +1,4 @@ -.bp-view-wrap { +.bp-view-engines-wrap { height: auto; display: flex; justify-content: space-around; diff --git a/ui/src/components/styles/CreateEditVdbeForm.css b/ui/src/components/styles/CreateEditVdbeForm.css new file mode 100644 index 00000000..159cd283 --- /dev/null +++ b/ui/src/components/styles/CreateEditVdbeForm.css @@ -0,0 +1,42 @@ +.create-edit-vdbe-form { + display: flex; + flex-direction: column; +} + +.create-edit-vdbe-form h2 { + margin-bottom: 20px; +} + +.cev-form-body { + display: flex; + flex-direction: row; +} + +.cev-form-fields { + display: flex; + flex-direction: column; + flex-grow: 3; + margin-bottom: 10px; +} + +.cev-form-fields .cev-field { + margin: 0 0 15px 0; +} + +.cev-preview { + flex-grow: 2; + display: flex; + justify-content: center; + margin-top: -20px; +} + +.cev-buttons { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; +} + +.cev-buttons button { + margin-left: 20px; +} diff --git a/ui/src/components/styles/ExpandableTableSet.css b/ui/src/components/styles/ExpandableTableSet.css new file mode 100644 index 00000000..86f5c507 --- /dev/null +++ b/ui/src/components/styles/ExpandableTableSet.css @@ -0,0 +1,91 @@ +.expandable-table-set-wrap { + display: flex; + flex-direction: column; + justify-content: center; +} + +.expandable-table-set { + width: 350px; +} + +.expandable-table-set.expandable { + overflow: hidden; + max-height: 180px; + mask-image: linear-gradient(180deg, #000 50%, transparent); +} + +.expandable-table-set-inner { + display: flex; + flex-direction: row; + flex-wrap: wrap; + width: 340px; + justify-content: space-between; + align-items: flex-start; +} + +.expandable-table-set-button { + display: flex; + justify-content: center; + align-items: center; + text-align: center; + cursor: pointer; + margin-top: 10px; + opacity: 0.35; + transition: all 0.25s; +} + +.expandable-table-set-button:hover { + opacity: 1; +} + +.show-more { + animation-duration: 0.75s; + animation-fill-mode: both; + animation-timing-function: ease-in-out; + animation-iteration-count: 1; +} + +.show-more:hover { + animation-name: bounce-down; +} + +.show-less { + animation-duration: 0.75s; + animation-fill-mode: both; + animation-timing-function: ease-in-out; + animation-iteration-count: 1; +} + +.show-less:hover { + animation-name: bounce-up; +} + +@keyframes bounce-down { + 0% { + transform: translateY(0); + } + 20% { + transform: translateY(3px); + } + 40% { + transform: translateY(1px); + } + 100% { + transform: translateY(1px); + } +} + +@keyframes bounce-up { + 0% { + transform: translateY(0); + } + 20% { + transform: translateY(-3px); + } + 40% { + transform: translateY(-1px); + } + 100% { + transform: translateY(-1px); + } +} diff --git a/ui/src/components/styles/InsetPanel.css b/ui/src/components/styles/InsetPanel.css new file mode 100644 index 00000000..891855b2 --- /dev/null +++ b/ui/src/components/styles/InsetPanel.css @@ -0,0 +1,15 @@ +.inset-panel-wrap { + display: flex; + flex-direction: column; + background-color: #f7f8f8; + border-radius: 19px; + padding: 29px; + margin-bottom: 19px; +} + +.inset-panel-wrap h2 { + display: flex; + align-items: center; + font-size: 1.5em; + margin: 0 0 5px 0; +} diff --git a/ui/src/components/styles/PerfView.css b/ui/src/components/styles/PerfView.css index 0fc10359..499d8b60 100644 --- a/ui/src/components/styles/PerfView.css +++ b/ui/src/components/styles/PerfView.css @@ -18,11 +18,13 @@ .perf-view-winsel-button { border-radius: 5px; width: 80px; + height: 100%; background-color: #f2f2f2; margin: 0 5px; border: 0; font-weight: 400; font-size: 0.8em; + cursor: pointer; } .perf-view-winsel-button:hover { diff --git a/ui/src/components/styles/TableView.css b/ui/src/components/styles/TableView.css index 2a3375ee..8b8ceaf0 100644 --- a/ui/src/components/styles/TableView.css +++ b/ui/src/components/styles/TableView.css @@ -1,12 +1,3 @@ -.db-table-set { - display: flex; - flex-direction: row; - flex-wrap: wrap; - width: 340px; - justify-content: space-between; - align-items: flex-start; -} - .db-table-view { width: 160px; height: 35px; diff --git a/ui/src/components/styles/VdbeView.css b/ui/src/components/styles/VdbeView.css index 71182e95..cbad6d78 100644 --- a/ui/src/components/styles/VdbeView.css +++ b/ui/src/components/styles/VdbeView.css @@ -24,3 +24,50 @@ font-size: 1.2em; } + +.vdbe-db-wrap { + position: relative; +} + +.vdbe-edit-controls { + position: absolute; + top: 0; + right: -60px; + display: flex; + flex-direction: column; + align-items: center; + opacity: 0; + transition: all 0.1s ease-in-out; +} + +.vdbe-edit-button { + opacity: 0.5; + transition: all 0.1s ease-in-out; +} + +.vdbe-edit-button:hover { + opacity: 1; +} + +.vdbe-view:hover .vdbe-edit-controls { + opacity: 1; +} + +.vdbe-endpoint { + display: flex; + flex-direction: row; + align-items: center; + font-size: 1em; + margin: 0 0 15px 0; + cursor: pointer; + color: #1d8033; + transition: all 0.2s ease-in-out; + + background-color: #f6fdf7; + border-radius: 20px; + padding: 3px 8px; +} + +.vdbe-endpoint:hover { + color: #51a965; +} diff --git a/ui/src/components/styles/VirtualInfraView.css b/ui/src/components/styles/VirtualInfraView.css index 31ac98af..7b5f4b98 100644 --- a/ui/src/components/styles/VirtualInfraView.css +++ b/ui/src/components/styles/VirtualInfraView.css @@ -1,5 +1,13 @@ -.vdbe-view-wrap { +.vdbe-view-engines-wrap { flex-grow: 1; display: flex; justify-content: space-around; } + +.infra-controls { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + margin: 20px 0 -20px 0; +} diff --git a/ui/src/components/styles/WorkloadInput.css b/ui/src/components/styles/WorkloadInput.css new file mode 100644 index 00000000..c91efaac --- /dev/null +++ b/ui/src/components/styles/WorkloadInput.css @@ -0,0 +1,26 @@ +.workload-input-sliders { + display: flex; + flex-direction: row; + justify-content: flex-start; + margin-bottom: 20px; +} + +.workload-slider { + max-width: 400px; + flex-grow: 1; + margin-right: 30px; +} + +.workload-slider h3 { + font-weight: 600; + margin: 10px 0 5px 0; +} + +.workload-input-buttons { + display: flex; + justify-content: flex-end; +} + +.workload-input-buttons button { + margin-left: 20px; +} diff --git a/ui/src/index.css b/ui/src/index.css deleted file mode 100644 index 6119ad9a..00000000 --- a/ui/src/index.css +++ /dev/null @@ -1,68 +0,0 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/ui/src/main.jsx b/ui/src/main.jsx index c9900533..1141dbf9 100644 --- a/ui/src/main.jsx +++ b/ui/src/main.jsx @@ -1,7 +1,6 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.jsx"; -import "./index.css"; ReactDOM.createRoot(document.getElementById("root")).render(