diff --git a/typescript/Frontend/package-lock.json b/typescript/Frontend/package-lock.json index d6a92a787..b73835d36 100644 --- a/typescript/Frontend/package-lock.json +++ b/typescript/Frontend/package-lock.json @@ -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", diff --git a/typescript/Frontend/package.json b/typescript/Frontend/package.json index 6767b52e0..636f207c8 100644 --- a/typescript/Frontend/package.json +++ b/typescript/Frontend/package.json @@ -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", diff --git a/typescript/Frontend/public/favicon.ico b/typescript/Frontend/public/favicon.ico index a11777cc4..58fe1e11c 100644 Binary files a/typescript/Frontend/public/favicon.ico and b/typescript/Frontend/public/favicon.ico differ diff --git a/typescript/Frontend/public/index.html b/typescript/Frontend/public/index.html index aa069f27c..1ae397d60 100644 --- a/typescript/Frontend/public/index.html +++ b/typescript/Frontend/public/index.html @@ -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`. --> - React App + Innovenergy diff --git a/typescript/Frontend/src/components/Installations/InstallationList.tsx b/typescript/Frontend/src/components/Installations/InstallationList.tsx index 297e942bd..79679e51e 100644 --- a/typescript/Frontend/src/components/Installations/InstallationList.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationList.tsx @@ -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" diff --git a/typescript/Frontend/src/components/Installations/Installations.tsx b/typescript/Frontend/src/components/Installations/Installations.tsx index 16602a401..4a40af6a7 100644 --- a/typescript/Frontend/src/components/Installations/Installations.tsx +++ b/typescript/Frontend/src/components/Installations/Installations.tsx @@ -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 ( @@ -20,6 +24,7 @@ const Installations = () => { diff --git a/typescript/Frontend/src/components/Installations/Log/CheckboxTree.tsx b/typescript/Frontend/src/components/Installations/Log/CheckboxTree.tsx index 502dd5f25..77ec72a1a 100644 --- a/typescript/Frontend/src/components/Installations/Log/CheckboxTree.tsx +++ b/typescript/Frontend/src/components/Installations/Log/CheckboxTree.tsx @@ -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 ( <> - {toggles !== null && routeMatch !== null && ( - } - defaultExpandIcon={} - sx={{ - height: 480, - flexGrow: 1, - overflow: "auto", - overflowX: "hidden", - }} - > - {renderTree(toggles)} - + <> + + } + defaultExpandIcon={} + sx={{ + flexGrow: 1, + overflow: "auto", + overflowX: "hidden", + position: ["sticky", "-webkit-sticky"], + top: 1, + }} + > + {renderTree(toggles)} + + )} ); diff --git a/typescript/Frontend/src/components/Installations/Log/DateRangePicker.tsx b/typescript/Frontend/src/components/Installations/Log/DateRangePicker.tsx new file mode 100644 index 000000000..559e100f8 --- /dev/null +++ b/typescript/Frontend/src/components/Installations/Log/DateRangePicker.tsx @@ -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>[] = [ + { + 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 ( + + + + ); +} diff --git a/typescript/Frontend/src/components/Installations/Log/Log.tsx b/typescript/Frontend/src/components/Installations/Log/Log.tsx index d1156f14e..a906996be 100644 --- a/typescript/Frontend/src/components/Installations/Log/Log.tsx +++ b/typescript/Frontend/src/components/Installations/Log/Log.tsx @@ -2,11 +2,7 @@ import React from "react"; import ScalarGraph from "./ScalarGraph"; const Log = () => { - return ( - <> - - - ); + return ; }; export default Log; diff --git a/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx b/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx index a0e5b9402..2747ae5ad 100644 --- a/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx +++ b/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx @@ -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 ( - { - 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 = !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 ( { range: range, type: "date", }, + yaxis: { + rangemode: "tozero", + }, + ...barGraphLayout, }} config={{ modeBarButtonsToRemove: [ @@ -278,6 +224,12 @@ const ScalarGraph = () => { /> ); }); + } + return ( + + Please make a selection on the left + + ); } }; return <>{renderGraphs()}; diff --git a/typescript/Frontend/src/components/Layout/Search.tsx b/typescript/Frontend/src/components/Layout/Search.tsx index c787d54e4..0aefd4811 100644 --- a/typescript/Frontend/src/components/Layout/Search.tsx +++ b/typescript/Frontend/src/components/Layout/Search.tsx @@ -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 ( -
+
{ + 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[] + ); +};