add example of daterangepicker, fix bugs

This commit is contained in:
Sina Blattmann 2023-05-22 11:24:38 +02:00
parent 0533a86b11
commit 9844c7a0ad
12 changed files with 405 additions and 152 deletions

View File

@ -15,6 +15,8 @@
"@mui/icons-material": "^5.11.0", "@mui/icons-material": "^5.11.0",
"@mui/lab": "^5.0.0-alpha.120", "@mui/lab": "^5.0.0-alpha.120",
"@mui/material": "^5.11.7", "@mui/material": "^5.11.7",
"@mui/x-date-pickers": "^6.5.0",
"@mui/x-date-pickers-pro": "^6.5.0",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@ -26,6 +28,7 @@
"axios": "^1.3.1", "axios": "^1.3.1",
"chart.js": "^4.2.1", "chart.js": "^4.2.1",
"css-loader": "^6.7.3", "css-loader": "^6.7.3",
"dayjs": "^1.11.7",
"formik": "^2.2.9", "formik": "^2.2.9",
"linq-to-typescript": "^11.0.0", "linq-to-typescript": "^11.0.0",
"package.json": "^2.0.1", "package.json": "^2.0.1",
@ -3091,9 +3094,9 @@
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.20.13", "version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
"integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.13.11" "regenerator-runtime": "^0.13.11"
}, },
@ -5015,13 +5018,13 @@
} }
}, },
"node_modules/@mui/utils": { "node_modules/@mui/utils": {
"version": "5.11.9", "version": "5.13.1",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.9.tgz", "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz",
"integrity": "sha512-eOJaqzcEs4qEwolcvFAmXGpln+uvouvOS9FUX6Wkrte+4I8rZbjODOBDVNlK+V6/ziTfD4iNKC0G+KfOTApbqg==", "integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.21.0",
"@types/prop-types": "^15.7.5", "@types/prop-types": "^15.7.5",
"@types/react-is": "^16.7.1 || ^17.0.0", "@types/react-is": "^18.2.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react-is": "^18.2.0" "react-is": "^18.2.0"
}, },
@ -5036,6 +5039,148 @@
"react": "^17.0.0 || ^18.0.0" "react": "^17.0.0 || ^18.0.0"
} }
}, },
"node_modules/@mui/x-date-pickers": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.5.0.tgz",
"integrity": "sha512-dRCO1mzHjfOqsa4LdKxiXQnV0cuGiAkliyxSDCdRn6clK2WdF9Oj+1+4Mkx7fcJA61SV1eP4Yg29s0/VDsZKZw==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"@mui/utils": "^5.12.3",
"@types/react-transition-group": "^4.4.6",
"clsx": "^1.2.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui"
},
"peerDependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/base": "^5.0.0-alpha.87",
"@mui/material": "^5.8.6",
"@mui/system": "^5.8.0",
"date-fns": "^2.25.0",
"date-fns-jalali": "^2.13.0-0",
"dayjs": "^1.10.7",
"luxon": "^3.0.2",
"moment": "^2.29.4",
"moment-hijri": "^2.1.2",
"moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
"react": "^17.0.2 || ^18.0.0",
"react-dom": "^17.0.2 || ^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
},
"date-fns": {
"optional": true
},
"date-fns-jalali": {
"optional": true
},
"dayjs": {
"optional": true
},
"luxon": {
"optional": true
},
"moment": {
"optional": true
},
"moment-hijri": {
"optional": true
},
"moment-jalaali": {
"optional": true
}
}
},
"node_modules/@mui/x-date-pickers-pro": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers-pro/-/x-date-pickers-pro-6.5.0.tgz",
"integrity": "sha512-3pwBsLe0xdtVJ+trFo872KkVE+0uOz5MXGihvsQf5CaEic3SWj/CX0g+k9rcZYndpntteghkMp5UnbSn/trTGA==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"@mui/utils": "^5.12.3",
"@mui/x-date-pickers": "6.5.0",
"@mui/x-license-pro": "6.0.4",
"clsx": "^1.2.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/base": "^5.0.0-alpha.87",
"@mui/material": "^5.8.6",
"@mui/system": "^5.8.0",
"date-fns": "^2.25.0",
"date-fns-jalali": "^2.13.0-0",
"dayjs": "^1.10.7",
"luxon": "^3.0.2",
"moment": "^2.29.4",
"moment-hijri": "^2.1.2",
"moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
"react": "^17.0.2 || ^18.0.0",
"react-dom": "^17.0.2 || ^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
},
"date-fns": {
"optional": true
},
"date-fns-jalali": {
"optional": true
},
"dayjs": {
"optional": true
},
"luxon": {
"optional": true
},
"moment": {
"optional": true
},
"moment-hijri": {
"optional": true
},
"moment-jalaali": {
"optional": true
}
}
},
"node_modules/@mui/x-license-pro": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/@mui/x-license-pro/-/x-license-pro-6.0.4.tgz",
"integrity": "sha512-4E9TRDb0Enc7OngSz/Lx34++WdiD95tsYuFCdhicq/FImahgLEg799qUj+il5UQJ5mxQP/pKdVqv0T8FZg2JPA==",
"dependencies": {
"@babel/runtime": "^7.21.0",
"@mui/utils": "^5.11.13"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": "^17.0.2 || ^18.0.0"
}
},
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1", "version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@ -6398,9 +6543,9 @@
} }
}, },
"node_modules/@types/react-is": { "node_modules/@types/react-is": {
"version": "17.0.3", "version": "18.2.0",
"resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", "integrity": "sha512-1vz2yObaQkLL7YFe/pme2cpvDsCwI1WXIfL+5eLz0MI9gFG24Re16RzUsI8t9XZn9ZWvgLNDrJBmrqXJO7GNQQ==",
"dependencies": { "dependencies": {
"@types/react": "*" "@types/react": "*"
} }
@ -6435,9 +6580,9 @@
} }
}, },
"node_modules/@types/react-transition-group": { "node_modules/@types/react-transition-group": {
"version": "4.4.5", "version": "4.4.6",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz",
"integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==",
"dependencies": { "dependencies": {
"@types/react": "*" "@types/react": "*"
} }
@ -9648,6 +9793,11 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
},
"node_modules/debounce": { "node_modules/debounce": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
@ -26996,9 +27146,9 @@
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
}, },
"@babel/runtime": { "@babel/runtime": {
"version": "7.20.13", "version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
"integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
"requires": { "requires": {
"regenerator-runtime": "^0.13.11" "regenerator-runtime": "^0.13.11"
} }
@ -28294,17 +28444,53 @@
"requires": {} "requires": {}
}, },
"@mui/utils": { "@mui/utils": {
"version": "5.11.9", "version": "5.13.1",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.9.tgz", "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz",
"integrity": "sha512-eOJaqzcEs4qEwolcvFAmXGpln+uvouvOS9FUX6Wkrte+4I8rZbjODOBDVNlK+V6/ziTfD4iNKC0G+KfOTApbqg==", "integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==",
"requires": { "requires": {
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.21.0",
"@types/prop-types": "^15.7.5", "@types/prop-types": "^15.7.5",
"@types/react-is": "^16.7.1 || ^17.0.0", "@types/react-is": "^18.2.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react-is": "^18.2.0" "react-is": "^18.2.0"
} }
}, },
"@mui/x-date-pickers": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.5.0.tgz",
"integrity": "sha512-dRCO1mzHjfOqsa4LdKxiXQnV0cuGiAkliyxSDCdRn6clK2WdF9Oj+1+4Mkx7fcJA61SV1eP4Yg29s0/VDsZKZw==",
"requires": {
"@babel/runtime": "^7.21.0",
"@mui/utils": "^5.12.3",
"@types/react-transition-group": "^4.4.6",
"clsx": "^1.2.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
}
},
"@mui/x-date-pickers-pro": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers-pro/-/x-date-pickers-pro-6.5.0.tgz",
"integrity": "sha512-3pwBsLe0xdtVJ+trFo872KkVE+0uOz5MXGihvsQf5CaEic3SWj/CX0g+k9rcZYndpntteghkMp5UnbSn/trTGA==",
"requires": {
"@babel/runtime": "^7.21.0",
"@mui/utils": "^5.12.3",
"@mui/x-date-pickers": "6.5.0",
"@mui/x-license-pro": "6.0.4",
"clsx": "^1.2.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
}
},
"@mui/x-license-pro": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/@mui/x-license-pro/-/x-license-pro-6.0.4.tgz",
"integrity": "sha512-4E9TRDb0Enc7OngSz/Lx34++WdiD95tsYuFCdhicq/FImahgLEg799qUj+il5UQJ5mxQP/pKdVqv0T8FZg2JPA==",
"requires": {
"@babel/runtime": "^7.21.0",
"@mui/utils": "^5.11.13"
}
},
"@nicolo-ribaudo/eslint-scope-5-internals": { "@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1", "version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@ -29391,9 +29577,9 @@
} }
}, },
"@types/react-is": { "@types/react-is": {
"version": "17.0.3", "version": "18.2.0",
"resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", "integrity": "sha512-1vz2yObaQkLL7YFe/pme2cpvDsCwI1WXIfL+5eLz0MI9gFG24Re16RzUsI8t9XZn9ZWvgLNDrJBmrqXJO7GNQQ==",
"requires": { "requires": {
"@types/react": "*" "@types/react": "*"
} }
@ -29428,9 +29614,9 @@
} }
}, },
"@types/react-transition-group": { "@types/react-transition-group": {
"version": "4.4.5", "version": "4.4.6",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz",
"integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==",
"requires": { "requires": {
"@types/react": "*" "@types/react": "*"
} }
@ -31904,6 +32090,11 @@
"whatwg-url": "^8.0.0" "whatwg-url": "^8.0.0"
} }
}, },
"dayjs": {
"version": "1.11.7",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz",
"integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
},
"debounce": { "debounce": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",

View File

@ -10,6 +10,8 @@
"@mui/icons-material": "^5.11.0", "@mui/icons-material": "^5.11.0",
"@mui/lab": "^5.0.0-alpha.120", "@mui/lab": "^5.0.0-alpha.120",
"@mui/material": "^5.11.7", "@mui/material": "^5.11.7",
"@mui/x-date-pickers": "^6.5.0",
"@mui/x-date-pickers-pro": "^6.5.0",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@ -21,6 +23,7 @@
"axios": "^1.3.1", "axios": "^1.3.1",
"chart.js": "^4.2.1", "chart.js": "^4.2.1",
"css-loader": "^6.7.3", "css-loader": "^6.7.3",
"dayjs": "^1.11.7",
"formik": "^2.2.9", "formik": "^2.2.9",
"linq-to-typescript": "^11.0.0", "linq-to-typescript": "^11.0.0",
"package.json": "^2.0.1", "package.json": "^2.0.1",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>Innovenergy</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -66,8 +66,12 @@ const InstallationList = (props: InstallationListProps) => {
overflow: "auto", overflow: "auto",
py: 0, py: 0,
mt: 1, mt: 1,
height: "80vh",
maxHeight: "70vh", maxHeight: "70vh",
height:
routeMatch?.pattern.path ===
routes.installations + routes.list + routes.log + ":id"
? "130px"
: "80vh",
}} }}
component="nav" component="nav"
aria-labelledby="nested-list-subheader" aria-labelledby="nested-list-subheader"

View File

@ -10,8 +10,12 @@ import InstallationList from "./InstallationList";
import Installation from "./Installation"; import Installation from "./Installation";
import CheckboxTree from "./Log/CheckboxTree"; import CheckboxTree from "./Log/CheckboxTree";
import LogContextProvider from "../Context/LogContextProvider"; import LogContextProvider from "../Context/LogContextProvider";
import useRouteMatch from "../../hooks/useRouteMatch";
const Installations = () => { const Installations = () => {
const routeMatch = useRouteMatch([
routes.installations + routes.list + routes.log + ":id",
]);
return ( return (
<InstallationContextProvider> <InstallationContextProvider>
<LogContextProvider> <LogContextProvider>
@ -20,6 +24,7 @@ const Installations = () => {
<SearchSidebar <SearchSidebar
id="installations-search-sidebar" id="installations-search-sidebar"
listComponent={InstallationList} listComponent={InstallationList}
height={routeMatch ? "200px" : undefined}
/> />
<CheckboxTree /> <CheckboxTree />
</Grid> </Grid>

View File

@ -31,10 +31,16 @@ const CheckboxTree = () => {
const handleCheckChildren = (children: TreeElement[], checked?: boolean) => { const handleCheckChildren = (children: TreeElement[], checked?: boolean) => {
if (children.length > 0) { if (children.length > 0) {
children.forEach((child) => { children.forEach((child) => {
setCheckedToggles((prevState) => ({ setCheckedToggles((prevState) => {
...prevState, console.log("setChecked", {
[child.id]: !checked, ...prevState,
})); [child.id]: !checked,
});
return {
...prevState,
[child.id]: !checked,
};
});
if (child.children.length > 0) { if (child.children.length > 0) {
handleCheckChildren(child.children, checked); handleCheckChildren(child.children, checked);
} }
@ -57,6 +63,7 @@ const CheckboxTree = () => {
event.stopPropagation(); event.stopPropagation();
}; };
const renderTree = (data: TreeElement[]): ReactNode => { const renderTree = (data: TreeElement[]): ReactNode => {
console.log("checkedToggles", checkedToggles);
return data.map((element) => { return data.map((element) => {
const checked = checkedToggles?.[element.id]; const checked = checkedToggles?.[element.id];
const splitName = element.name.split("/"); const splitName = element.name.split("/");
@ -84,23 +91,27 @@ const CheckboxTree = () => {
); );
}); });
}; };
return ( return (
<> <>
<Divider sx={{ mt: 2 }} />
{toggles !== null && routeMatch !== null && ( {toggles !== null && routeMatch !== null && (
<TreeView <>
aria-label="rich object" <Divider sx={{ mt: 2 }} />
defaultCollapseIcon={<ExpandMoreIcon />} <TreeView
defaultExpandIcon={<ChevronRightIcon />} aria-label="rich object"
sx={{ defaultCollapseIcon={<ExpandMoreIcon />}
height: 480, defaultExpandIcon={<ChevronRightIcon />}
flexGrow: 1, sx={{
overflow: "auto", flexGrow: 1,
overflowX: "hidden", overflow: "auto",
}} overflowX: "hidden",
> position: ["sticky", "-webkit-sticky"],
{renderTree(toggles)} top: 1,
</TreeView> }}
>
{renderTree(toggles)}
</TreeView>
</>
)} )}
</> </>
); );

View File

@ -0,0 +1,62 @@
import * as React from "react";
import dayjs, { Dayjs } from "dayjs";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { StaticDateRangePicker } from "@mui/x-date-pickers-pro/StaticDateRangePicker";
import { PickersShortcutsItem } from "@mui/x-date-pickers/PickersShortcuts";
import { DateRange } from "@mui/x-date-pickers-pro";
const shortcutsItems: PickersShortcutsItem<DateRange<Dayjs>>[] = [
{
label: "This Week",
getValue: () => {
const today = dayjs();
return [today.startOf("week"), today.endOf("week")];
},
},
{
label: "Last Week",
getValue: () => {
const today = dayjs();
const prevWeek = today.subtract(7, "day");
return [prevWeek.startOf("week"), prevWeek.endOf("week")];
},
},
{
label: "Last 7 Days",
getValue: () => {
const today = dayjs();
return [today.subtract(7, "day"), today];
},
},
{
label: "Current Month",
getValue: () => {
const today = dayjs();
return [today.startOf("month"), today.endOf("month")];
},
},
{
label: "Next Month",
getValue: () => {
const today = dayjs();
const startOfNextMonth = today.endOf("month").add(1, "day");
return [startOfNextMonth, startOfNextMonth.endOf("month")];
},
},
{ label: "Reset", getValue: () => [null, null] },
];
export default function BasicRangeShortcuts() {
return (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<StaticDateRangePicker
slotProps={{
shortcuts: {
items: shortcutsItems,
},
}}
/>
</LocalizationProvider>
);
}

View File

@ -2,11 +2,7 @@ import React from "react";
import ScalarGraph from "./ScalarGraph"; import ScalarGraph from "./ScalarGraph";
const Log = () => { const Log = () => {
return ( return <ScalarGraph />;
<>
<ScalarGraph />
</>
);
}; };
export default Log; export default Log;

View File

@ -1,13 +1,11 @@
import Plot from "react-plotly.js"; import Plot from "react-plotly.js";
import { RecordSeries } from "../../../dataCache/data"; import { RecordSeries } from "../../../dataCache/data";
import { import {
GraphCoordinates,
GraphData, GraphData,
flattenBarGraphData, createTimes,
flattenToggles, flattenToggles,
insertTreeElements, insertTreeElements,
isNumeric, isNumeric,
isText,
parseCsv, parseCsv,
stringToColor, stringToColor,
transformToBarGraphData, transformToBarGraphData,
@ -18,28 +16,17 @@ import { BehaviorSubject, startWith, throttleTime, withLatestFrom } from "rxjs";
import { S3Access } from "../../../dataCache/S3/S3Access"; import { S3Access } from "../../../dataCache/S3/S3Access";
import DataCache, { FetchResult } from "../../../dataCache/dataCache"; import DataCache, { FetchResult } from "../../../dataCache/dataCache";
import { LogContext } from "../../Context/LogContextProvider"; import { LogContext } from "../../Context/LogContextProvider";
import { TreeElement, ToggleElement } from "./CheckboxTree"; import { TreeElement } from "./CheckboxTree";
import { isDefined } from "../../../dataCache/utils/maybe"; import { isDefined } from "../../../dataCache/utils/maybe";
import { Data, Layout } from "plotly.js";
import { Data } from "plotly.js"; import { Alert } from "@mui/material";
export const createTimes = (
range: TimeRange,
numberOfNodes: number
): UnixTime[] => {
const oneSpan = range.duration.divide(numberOfNodes);
const roundedRange = TimeRange.fromTimes(
range.start.round(oneSpan),
range.end.round(oneSpan)
);
return roundedRange.sample(oneSpan);
};
const NUMBER_OF_NODES = 100; const NUMBER_OF_NODES = 100;
const ScalarGraph = () => { const ScalarGraph = () => {
const timeRange = createTimes( const timeRange = createTimes(
UnixTime.now() /* .fromTicks(1682085650) */ UnixTime.now() /* .fromTicks(1682085650) */
.earlier(TimeSpan.fromDays(2))
.rangeBefore(TimeSpan.fromDays(4)), .rangeBefore(TimeSpan.fromDays(4)),
NUMBER_OF_NODES NUMBER_OF_NODES
); );
@ -78,19 +65,12 @@ const ScalarGraph = () => {
const toggleValues = timeSeries.find((timeStamp) => timeStamp.value); const toggleValues = timeSeries.find((timeStamp) => timeStamp.value);
if (toggles === null && toggleValues && toggleValues.value) { if (toggles === null && toggleValues && toggleValues.value) {
const treeElements = Object.keys(toggleValues.value) const treeElements = Object.keys(toggleValues.value)
.map((path) => { .map((path) =>
return path path
.split("/") .split("/")
.map( .map((_, i, arr) => `/${arr.slice(1, i + 1).join("/")}`)
(split, i) => .slice(1)
"/" + )
path
.split("/")
.slice(1, i + 1)
.join("/")
)
.slice(1);
})
.reduce( .reduce(
(children, path) => insertTreeElements(children, path), (children, path) => insertTreeElements(children, path),
[] as TreeElement[] [] as TreeElement[]
@ -139,6 +119,7 @@ const ScalarGraph = () => {
transformedObject[key] = { transformedObject[key] = {
x: [], x: [],
y: [], y: [],
marker: { color: stringToColor(key) },
}; };
} }
transformedObject[key].x.push( transformedObject[key].x.push(
@ -147,7 +128,10 @@ const ScalarGraph = () => {
transformedObject[key].y.push(item.value?.[key]); transformedObject[key].y.push(item.value?.[key]);
}); });
} }
if (plotTitles.length === 0) { if (
plotTitles.length === 0 &&
Object.keys(transformedObject).length > 0
) {
setPlotTitles(Object.keys(transformedObject)); setPlotTitles(Object.keys(transformedObject));
} }
}); });
@ -168,76 +152,34 @@ const ScalarGraph = () => {
const renderGraphs = () => { const renderGraphs = () => {
if (checkedToggles) { if (checkedToggles) {
const coordinateTimeSeries = transformToGraphData(timeSeries); const coordinateTimeSeries = transformToGraphData(timeSeries);
if (plotTitles.length === 0) { const visibleGraphs = Object.keys(coordinateTimeSeries).filter((path) => {
setPlotTitles(Object.keys(coordinateTimeSeries)); return checkedToggles[path];
} });
return Object.keys(coordinateTimeSeries) if (visibleGraphs.length > 0) {
.filter((path) => { return visibleGraphs.map((path) => {
return checkedToggles[path]; const isScalar = isNumeric(coordinateTimeSeries[path].y[0]);
}) const data = isScalar
.map((path) => { ? [
const data = coordinateTimeSeries[path] ?? { x: [], y: [] }; {
const isScalar = isNumeric(data.y[0]); ...coordinateTimeSeries[path],
if (!isScalar) { type: "scatter",
const barGraphData = transformToBarGraphData(data); mode: "lines+markers",
return ( fill: "tozeroy",
<Plot },
key={path} ]
data={barGraphData as Data[]} : transformToBarGraphData(coordinateTimeSeries[path]);
layout={{ const barGraphLayout: Partial<Layout> = !isScalar
height: 500, ? {
width: 1000, bargap: 0,
title: path, barmode: "stack",
uirevision: uiRevision, barnorm: "percent",
xaxis: { }
autorange: false, : {};
range: range,
type: "date",
},
bargap: 0,
barmode: "stack",
barnorm: "percent",
}}
config={{
modeBarButtonsToRemove: [
"lasso2d",
"select2d",
"pan2d",
"autoScale2d",
],
}}
onRelayout={(params) => {
const xaxisRange0 = params["xaxis.range[0]"];
const xaxisRange1 = params["xaxis.range[1]"];
if (xaxisRange0 && xaxisRange1) {
setRange([new Date(xaxisRange0), new Date(xaxisRange1)]);
setUiRevision(Math.random());
const times = createTimes(
TimeRange.fromTimes(
UnixTime.fromDate(new Date(xaxisRange0)),
UnixTime.fromDate(new Date(xaxisRange1))
),
NUMBER_OF_NODES
);
cache.getSeries(times);
times$.next(times);
}
}}
/>
);
}
return ( return (
<Plot <Plot
key={path} key={path}
data={[ data={data as Data[]}
{
...data,
type: isScalar ? "scatter" : "bar",
mode: "lines+markers",
marker: { color: "red" },
},
]}
layout={{ layout={{
width: 1000, width: 1000,
height: 500, height: 500,
@ -248,6 +190,10 @@ const ScalarGraph = () => {
range: range, range: range,
type: "date", type: "date",
}, },
yaxis: {
rangemode: "tozero",
},
...barGraphLayout,
}} }}
config={{ config={{
modeBarButtonsToRemove: [ modeBarButtonsToRemove: [
@ -278,6 +224,12 @@ const ScalarGraph = () => {
/> />
); );
}); });
}
return (
<Alert sx={{ mt: 2 }} severity="info">
Please make a selection on the left
</Alert>
);
} }
}; };
return <>{renderGraphs()}</>; return <>{renderGraphs()}</>;

View File

@ -5,14 +5,15 @@ import { useIntl } from "react-intl";
interface SearchSidebarProps { interface SearchSidebarProps {
listComponent: FC<{ searchQuery: string }>; listComponent: FC<{ searchQuery: string }>;
id: string; id: string;
height?: string;
} }
const SearchSidebar = (props: SearchSidebarProps) => { const SearchSidebar = (props: SearchSidebarProps) => {
const { listComponent: ListComponent, id } = props; const { listComponent: ListComponent, id, height } = props;
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const intl = useIntl(); const intl = useIntl();
return ( return (
<div style={{ height: "500px", overflow: "hidden" }}> <div style={{ height: height ?? "500px", overflow: "hidden" }}>
<TextField <TextField
id={id} id={id}
label={intl.formatMessage({ label={intl.formatMessage({

View File

@ -3,6 +3,8 @@ import {
TreeElement, TreeElement,
ToggleElement, ToggleElement,
} from "../components/Installations/Log/CheckboxTree"; } from "../components/Installations/Log/CheckboxTree";
import { TimeRange, UnixTime } from "../dataCache/time";
import { DataRecord } from "../dataCache/data";
export interface GraphCoordinates { export interface GraphCoordinates {
x: Datum[] | Datum[][] | TypedArray; x: Datum[] | Datum[][] | TypedArray;
@ -13,8 +15,20 @@ export interface GraphCoordinates {
marker?: { color: string }; marker?: { color: string };
type?: string; type?: string;
name?: string; name?: string;
mode?: string;
} }
export const createTimes = (
range: TimeRange,
numberOfNodes: number
): UnixTime[] => {
const oneSpan = range.duration.divide(numberOfNodes);
const roundedRange = TimeRange.fromTimes(
range.start.round(oneSpan),
range.end.round(oneSpan)
);
return roundedRange.sample(oneSpan);
};
export interface GraphData { export interface GraphData {
[path: string]: GraphCoordinates; [path: string]: GraphCoordinates;
} }
@ -130,3 +144,17 @@ export const transformToBarGraphData = (data: GraphCoordinates) => {
}); });
return flattenBarGraphData(barGraphData); return flattenBarGraphData(barGraphData);
}; };
export const getTreeElements = (toggleValues: DataRecord) => {
return Object.keys(toggleValues)
.map((path) =>
path
.split("/")
.map((_, i, arr) => `/${arr.slice(1, i + 1).join("/")}`)
.slice(1)
)
.reduce(
(children, path) => insertTreeElements(children, path),
[] as TreeElement[]
);
};