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/lab": "^5.0.0-alpha.120",
"@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/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
@ -26,6 +28,7 @@
"axios": "^1.3.1",
"chart.js": "^4.2.1",
"css-loader": "^6.7.3",
"dayjs": "^1.11.7",
"formik": "^2.2.9",
"linq-to-typescript": "^11.0.0",
"package.json": "^2.0.1",
@ -3091,9 +3094,9 @@
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
},
"node_modules/@babel/runtime": {
"version": "7.20.13",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
"integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
"integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
"dependencies": {
"regenerator-runtime": "^0.13.11"
},
@ -5015,13 +5018,13 @@
}
},
"node_modules/@mui/utils": {
"version": "5.11.9",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.9.tgz",
"integrity": "sha512-eOJaqzcEs4qEwolcvFAmXGpln+uvouvOS9FUX6Wkrte+4I8rZbjODOBDVNlK+V6/ziTfD4iNKC0G+KfOTApbqg==",
"version": "5.13.1",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz",
"integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@babel/runtime": "^7.21.0",
"@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",
"react-is": "^18.2.0"
},
@ -5036,6 +5039,148 @@
"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": {
"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",
@ -6398,9 +6543,9 @@
}
},
"node_modules/@types/react-is": {
"version": "17.0.3",
"resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz",
"integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==",
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-1vz2yObaQkLL7YFe/pme2cpvDsCwI1WXIfL+5eLz0MI9gFG24Re16RzUsI8t9XZn9ZWvgLNDrJBmrqXJO7GNQQ==",
"dependencies": {
"@types/react": "*"
}
@ -6435,9 +6580,9 @@
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==",
"version": "4.4.6",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz",
"integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==",
"dependencies": {
"@types/react": "*"
}
@ -9648,6 +9793,11 @@
"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": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
@ -26996,9 +27146,9 @@
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
},
"@babel/runtime": {
"version": "7.20.13",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
"integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
"integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
"requires": {
"regenerator-runtime": "^0.13.11"
}
@ -28294,17 +28444,53 @@
"requires": {}
},
"@mui/utils": {
"version": "5.11.9",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.9.tgz",
"integrity": "sha512-eOJaqzcEs4qEwolcvFAmXGpln+uvouvOS9FUX6Wkrte+4I8rZbjODOBDVNlK+V6/ziTfD4iNKC0G+KfOTApbqg==",
"version": "5.13.1",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz",
"integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==",
"requires": {
"@babel/runtime": "^7.20.13",
"@babel/runtime": "^7.21.0",
"@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",
"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": {
"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",
@ -29391,9 +29577,9 @@
}
},
"@types/react-is": {
"version": "17.0.3",
"resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz",
"integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==",
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-1vz2yObaQkLL7YFe/pme2cpvDsCwI1WXIfL+5eLz0MI9gFG24Re16RzUsI8t9XZn9ZWvgLNDrJBmrqXJO7GNQQ==",
"requires": {
"@types/react": "*"
}
@ -29428,9 +29614,9 @@
}
},
"@types/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==",
"version": "4.4.6",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz",
"integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==",
"requires": {
"@types/react": "*"
}
@ -31904,6 +32090,11 @@
"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": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",

View File

@ -10,6 +10,8 @@
"@mui/icons-material": "^5.11.0",
"@mui/lab": "^5.0.0-alpha.120",
"@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/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
@ -21,6 +23,7 @@
"axios": "^1.3.1",
"chart.js": "^4.2.1",
"css-loader": "^6.7.3",
"dayjs": "^1.11.7",
"formik": "^2.2.9",
"linq-to-typescript": "^11.0.0",
"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.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Innovenergy</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,8 @@ import {
TreeElement,
ToggleElement,
} from "../components/Installations/Log/CheckboxTree";
import { TimeRange, UnixTime } from "../dataCache/time";
import { DataRecord } from "../dataCache/data";
export interface GraphCoordinates {
x: Datum[] | Datum[][] | TypedArray;
@ -13,8 +15,20 @@ export interface GraphCoordinates {
marker?: { color: string };
type?: 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 {
[path: string]: GraphCoordinates;
}
@ -130,3 +144,17 @@ export const transformToBarGraphData = (data: GraphCoordinates) => {
});
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[]
);
};