From c5287ce95f369c33db06d55b274ebde5a14b515d Mon Sep 17 00:00:00 2001 From: Sina Blattmann Date: Mon, 13 Mar 2023 15:17:40 +0100 Subject: [PATCH] add context for state management for installation and group --- typescript/Frontend/package-lock.json | 44 ------- typescript/Frontend/package.json | 1 - .../Context/GroupContextProvider.tsx | 51 ++++++++ .../Context/InstallationContextProvider.tsx | 43 +++++++ .../Frontend/src/components/Groups/Folder.tsx | 3 +- .../src/components/Groups/FolderForm.tsx | 6 +- .../Frontend/src/components/Groups/Groups.tsx | 45 ++++--- .../Groups/Tree/GroupDataContext.tsx | 13 --- .../src/components/Groups/Tree/GroupTree.tsx | 40 ++----- .../Groups/Tree/TreeNode.module.scss | 4 + .../src/components/Groups/Tree/TreeNode.tsx | 19 ++- .../components/Installations/CustomerForm.tsx | 23 +++- .../components/Installations/Installation.tsx | 4 +- .../Installations/InstallationList.tsx | 110 +++++++++--------- .../Installations/Installations.tsx | 45 +++---- 15 files changed, 248 insertions(+), 203 deletions(-) create mode 100644 typescript/Frontend/src/components/Context/GroupContextProvider.tsx create mode 100644 typescript/Frontend/src/components/Context/InstallationContextProvider.tsx delete mode 100644 typescript/Frontend/src/components/Groups/Tree/GroupDataContext.tsx diff --git a/typescript/Frontend/package-lock.json b/typescript/Frontend/package-lock.json index 1cdc6cf7e..1bcf739ce 100644 --- a/typescript/Frontend/package-lock.json +++ b/typescript/Frontend/package-lock.json @@ -26,7 +26,6 @@ "chart.js": "^4.2.1", "css-loader": "^6.7.3", "formik": "^2.2.9", - "mobx-react-lite": "^3.4.3", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", "react-dnd": "^16.0.1", @@ -13219,37 +13218,6 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/mobx": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.8.0.tgz", - "integrity": "sha512-+o/DrHa4zykFMSKfS8Z+CPSEg5LW9tSNGTuN8o6MF1GKxlfkSHSeJn5UtgxvPkGgaouplnrLXCF+duAsmm6FHQ==", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mobx" - } - }, - "node_modules/mobx-react-lite": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.4.3.tgz", - "integrity": "sha512-NkJREyFTSUXR772Qaai51BnE1voWx56LOL80xG7qkZr6vo8vEaLF3sz1JNUVh+rxmUzxYaqOhfuxTfqUh0FXUg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mobx" - }, - "peerDependencies": { - "mobx": "^6.1.0", - "react": "^16.8.0 || ^17 || ^18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -28182,18 +28150,6 @@ "minimist": "^1.2.6" } }, - "mobx": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.8.0.tgz", - "integrity": "sha512-+o/DrHa4zykFMSKfS8Z+CPSEg5LW9tSNGTuN8o6MF1GKxlfkSHSeJn5UtgxvPkGgaouplnrLXCF+duAsmm6FHQ==", - "peer": true - }, - "mobx-react-lite": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.4.3.tgz", - "integrity": "sha512-NkJREyFTSUXR772Qaai51BnE1voWx56LOL80xG7qkZr6vo8vEaLF3sz1JNUVh+rxmUzxYaqOhfuxTfqUh0FXUg==", - "requires": {} - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/typescript/Frontend/package.json b/typescript/Frontend/package.json index d1fd92422..803841d8f 100644 --- a/typescript/Frontend/package.json +++ b/typescript/Frontend/package.json @@ -21,7 +21,6 @@ "chart.js": "^4.2.1", "css-loader": "^6.7.3", "formik": "^2.2.9", - "mobx-react-lite": "^3.4.3", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", "react-dnd": "^16.0.1", diff --git a/typescript/Frontend/src/components/Context/GroupContextProvider.tsx b/typescript/Frontend/src/components/Context/GroupContextProvider.tsx new file mode 100644 index 000000000..8201a12ff --- /dev/null +++ b/typescript/Frontend/src/components/Context/GroupContextProvider.tsx @@ -0,0 +1,51 @@ +import { createContext, ReactNode, useCallback, useState } from "react"; +import axiosConfig from "../../config/axiosConfig"; +import { I_Folder, I_Installation } from "../../util/types"; + +interface GroupContextProviderProps { + data: (I_Folder | I_Installation)[]; + setData: (value: (I_Folder | I_Installation)[]) => void; + fetchData: () => Promise; + loading: boolean; + setLoading: (value: boolean) => void; + getError: boolean; +} + +export const GroupContext = createContext({ + data: [], + setData: () => {}, + fetchData: () => Promise.resolve(), + loading: false, + setLoading: () => {}, + getError: false, +}); + +const GroupContextProvider = ({ children }: { children: ReactNode }) => { + const [data, setData] = useState<(I_Folder | I_Installation)[]>([]); + const [loading, setLoading] = useState(false); + const [getError, setGetError] = useState(false); + + const fetchData = useCallback(async () => { + setLoading(true); + return axiosConfig + .get("/GetAllFoldersAndInstallations") + .then((res) => { + setData(res.data); + setLoading(false); + }) + .catch((err) => { + setGetError(err); + setLoading(false); + }); + }, [setData]); + + return ( + + {children} + + ); +}; + +export default GroupContextProvider; diff --git a/typescript/Frontend/src/components/Context/InstallationContextProvider.tsx b/typescript/Frontend/src/components/Context/InstallationContextProvider.tsx new file mode 100644 index 000000000..cf09fd57e --- /dev/null +++ b/typescript/Frontend/src/components/Context/InstallationContextProvider.tsx @@ -0,0 +1,43 @@ +import { createContext, ReactNode, useCallback, useState } from "react"; +import axiosConfig from "../../config/axiosConfig"; +import { I_Installation } from "../../util/types"; + +interface InstallationContextProviderProps { + data: I_Installation[]; + setData: (value: I_Installation[]) => void; + fetchData: () => Promise; + loading: boolean; + setLoading: (value: boolean) => void; +} + +export const InstallationContext = + createContext({ + data: [], + setData: () => {}, + fetchData: () => Promise.resolve(), + loading: false, + setLoading: () => {}, + }); + +const InstallationContextProvider = ({ children }: { children: ReactNode }) => { + const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); + + const fetchData = useCallback(async () => { + setLoading(true); + axiosConfig.get("/GetAllInstallations", {}).then((res) => { + setData(res.data); + setLoading(false); + }); + }, []); + + return ( + + {children} + + ); +}; + +export default InstallationContextProvider; diff --git a/typescript/Frontend/src/components/Groups/Folder.tsx b/typescript/Frontend/src/components/Groups/Folder.tsx index c767bc533..3215d7564 100644 --- a/typescript/Frontend/src/components/Groups/Folder.tsx +++ b/typescript/Frontend/src/components/Groups/Folder.tsx @@ -1,11 +1,10 @@ import { Box, CircularProgress, Alert } from "@mui/material"; import { AxiosError } from "axios"; -import { useState, useEffect, useContext } from "react"; +import { useState, useEffect } from "react"; import { useParams } from "react-router-dom"; import axiosConfig from "../../config/axiosConfig"; import { I_Installation } from "../../util/types"; import FolderForm from "./FolderForm"; -import GroupDataContext from "./Tree/GroupDataContext"; const Folder = () => { const { id } = useParams(); diff --git a/typescript/Frontend/src/components/Groups/FolderForm.tsx b/typescript/Frontend/src/components/Groups/FolderForm.tsx index 5611dfc38..40c2bf57a 100644 --- a/typescript/Frontend/src/components/Groups/FolderForm.tsx +++ b/typescript/Frontend/src/components/Groups/FolderForm.tsx @@ -1,11 +1,12 @@ import { Button, CircularProgress, Grid } from "@mui/material"; import { useFormik } from "formik"; -import { useState } from "react"; +import { useContext, useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import axiosConfig from "../../config/axiosConfig"; import { I_Folder } from "../../util/types"; import InnovenergySnackbar from "../InnovenergySnackbar"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; +import { GroupContext } from "../Context/GroupContextProvider"; interface I_CustomerFormProps { values: I_Folder; @@ -16,6 +17,8 @@ const updateFolder = (data: I_Folder) => { return axiosConfig.put("/UpdateFolder", data); }; const FolderForm = (props: I_CustomerFormProps) => { + const { fetchData } = useContext(GroupContext); + const { values, id } = props; const intl = useIntl(); @@ -39,6 +42,7 @@ const FolderForm = (props: I_CustomerFormProps) => { .then((res) => { setSnackbarOpen(true); setLoading(false); + fetchData(); }) .catch((err) => { setSnackbarOpen(true); diff --git a/typescript/Frontend/src/components/Groups/Groups.tsx b/typescript/Frontend/src/components/Groups/Groups.tsx index 71b0392b8..3fb4b6764 100644 --- a/typescript/Frontend/src/components/Groups/Groups.tsx +++ b/typescript/Frontend/src/components/Groups/Groups.tsx @@ -1,38 +1,37 @@ import { Grid } from "@mui/material"; import { Container } from "@mui/system"; -import { useState } from "react"; import { Routes, Route } from "react-router"; import routes from "../../routes.json"; -import { I_Folder, I_Installation } from "../../util/types"; -import InstallationDetail from "../Installations/Installation"; +import Installation from "../Installations/Installation"; import NavigationButtons from "../Layout/NavigationButtons"; import Folder from "./Folder"; import GroupTabs from "./GroupTabs"; +import GroupContextProvider from "../Context/GroupContextProvider"; import GroupTree from "./Tree/GroupTree"; const Groups = () => { - const [data, setData] = useState<(I_Folder | I_Installation)[]>(); - return ( - - - - - + + + + + + + + + + + } index /> + Users} /> + } + /> + + - - - - } index /> - Users} /> - } - /> - - - - + + ); }; diff --git a/typescript/Frontend/src/components/Groups/Tree/GroupDataContext.tsx b/typescript/Frontend/src/components/Groups/Tree/GroupDataContext.tsx deleted file mode 100644 index 9134d4c9a..000000000 --- a/typescript/Frontend/src/components/Groups/Tree/GroupDataContext.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { createContext } from "react"; -import { I_Folder, I_Installation } from "../../../util/types"; - -interface GroupData { - data: (I_Folder | I_Installation)[] | undefined; - setData: (value: (I_Folder | I_Installation)[]) => void; -} -const GroupDataContext = createContext({ - setData: (value) => {}, - data: [], -}); - -export default GroupDataContext; diff --git a/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx b/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx index df8868f10..bdc4c35ee 100644 --- a/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx +++ b/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from "react"; +import { useEffect, useState, useContext } from "react"; import axiosConfig from "../../../config/axiosConfig"; import { I_Folder, I_Installation } from "../../../util/types"; import { Alert, CircularProgress, Grid } from "@mui/material"; @@ -15,6 +15,7 @@ import styles from "./GroupTree.module.scss"; import withScrolling from "react-dnd-scrolling"; import DragPreview from "./DragPreview"; import InnovenergySnackbar from "../../InnovenergySnackbar"; +import { GroupContext } from "../../Context/GroupContextProvider"; const getTreeData = ( data: (I_Folder | I_Installation)[] @@ -31,40 +32,19 @@ const getTreeData = ( }); }; -interface GroupTreeProps { - data: (I_Folder | I_Installation)[] | undefined; - setData: (value: (I_Folder | I_Installation)[]) => void; -} - -const GroupTree = (props: GroupTreeProps) => { - const { data, setData } = props; - const [loading, setLoading] = useState(false); - const [getError, setGetError] = useState(false); +const GroupTree = () => { + const { data, fetchData, loading, setLoading, getError } = + useContext(GroupContext); const [putError, setPutError] = useState(false); - const [snackbarOpen, setSnackbarOpen] = useState(false); const ScrollingComponent = withScrolling("div"); - const getData = useCallback(async () => { - setLoading(true); - return axiosConfig - .get("/GetAllFoldersAndInstallations") - .then((res) => { - setData(res.data); - setLoading(false); - }) - .catch((err) => { - setGetError(err); - setLoading(false); - }); - }, [setData]); - useEffect(() => { - getData(); - }, [getData]); + fetchData(); + }, [fetchData]); - const findParent = (element: I_Folder | I_Installation): any => { + /* const findParent = (element: I_Folder | I_Installation): any => { if (data) { const parent = data.find( (el) => el.type === "Folder" && el.id === element.parentId @@ -74,7 +54,7 @@ const GroupTree = (props: GroupTreeProps) => { } return element.parentId; } - }; + }; */ const handleDrop = ( newTree: NodeModel[], @@ -92,7 +72,7 @@ const GroupTree = (props: GroupTreeProps) => { ) .then(() => { setSnackbarOpen(true); - getData(); + fetchData(); }) .catch((err) => { setPutError(err); diff --git a/typescript/Frontend/src/components/Groups/Tree/TreeNode.module.scss b/typescript/Frontend/src/components/Groups/Tree/TreeNode.module.scss index e36b7be8c..39b7f8f00 100644 --- a/typescript/Frontend/src/components/Groups/Tree/TreeNode.module.scss +++ b/typescript/Frontend/src/components/Groups/Tree/TreeNode.module.scss @@ -34,3 +34,7 @@ .handle > svg { pointer-events: none; } + +.selected { + background-color: #f5910014; +} diff --git a/typescript/Frontend/src/components/Groups/Tree/TreeNode.tsx b/typescript/Frontend/src/components/Groups/Tree/TreeNode.tsx index 5047e07ea..9613e77e6 100644 --- a/typescript/Frontend/src/components/Groups/Tree/TreeNode.tsx +++ b/typescript/Frontend/src/components/Groups/Tree/TreeNode.tsx @@ -8,20 +8,27 @@ import routes from "../../../routes.json"; import { I_Folder, I_Installation } from "../../../util/types"; import TypeIcon from "../TypeIcon"; import DragHandleIcon from "@mui/icons-material/DragHandle"; +import useRouteMatch from "../../../hooks/useRouteMatch"; -type Props = { +interface TreeNodeProps { node: NodeModel; depth: number; isOpen: boolean; onToggle: (id: NodeModel["id"]) => void; hasChild: boolean; handleRef: React.RefObject; -}; +} -const TreeNode: React.FC = (props) => { +const TreeNode = (props: TreeNodeProps) => { const { node, isOpen, hasChild, onToggle, depth, handleRef } = props; const indent = depth * 24; + const routeMatch = useRouteMatch([ + routes.groups + routes.installation + ":id", + routes.groups + routes.folder + ":id", + routes.groups + routes.users + ":id", + ]); + const handleToggle = (e: React.MouseEvent) => { e.stopPropagation(); onToggle(node.id); @@ -29,7 +36,11 @@ const TreeNode: React.FC = (props) => { return (
{ const { values, id } = props; - const intl = useIntl(); - const [open, setOpen] = useState(false); + const intl = useIntl(); + const { fetchData: fetchGroupData } = useContext(GroupContext); + const { fetchData: fetchInstallationData } = useContext(InstallationContext); + + const groupsMatch = useRouteMatch([ + routes.groups + routes.installation + ":id", + ]); + const formik = useFormik({ initialValues: { name: values.name, @@ -30,8 +40,13 @@ const CustomerForm = (props: I_CustomerFormProps) => { ...formikValues, id, }) - .then((res) => { + .then(() => { setOpen(true); + if (groupsMatch) { + fetchGroupData(); + } else { + fetchInstallationData(); + } }); }, }); diff --git a/typescript/Frontend/src/components/Installations/Installation.tsx b/typescript/Frontend/src/components/Installations/Installation.tsx index de6f78824..9abedf62a 100644 --- a/typescript/Frontend/src/components/Installations/Installation.tsx +++ b/typescript/Frontend/src/components/Installations/Installation.tsx @@ -6,7 +6,7 @@ import CustomerForm from "./CustomerForm"; import axiosConfig from "../../config/axiosConfig"; import { I_Installation } from "../../util/types"; -const InstallationDetail = () => { +const Installation = () => { const { id } = useParams(); const [values, setValues] = useState(); const [loading, setLoading] = useState(false); @@ -50,4 +50,4 @@ const InstallationDetail = () => { return null; }; -export default InstallationDetail; +export default Installation; diff --git a/typescript/Frontend/src/components/Installations/InstallationList.tsx b/typescript/Frontend/src/components/Installations/InstallationList.tsx index b145cd0b3..091f359ea 100644 --- a/typescript/Frontend/src/components/Installations/InstallationList.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationList.tsx @@ -5,9 +5,9 @@ import { CircularProgress, Divider, Grid } from "@mui/material"; import { Link } from "react-router-dom"; import useRouteMatch from "../../hooks/useRouteMatch"; import routes from "../../routes.json"; -import { Fragment, useEffect, useState } from "react"; +import { Fragment, useContext, useEffect } from "react"; import { I_Installation } from "../../util/types"; -import axiosConfig from "../../config/axiosConfig"; +import { InstallationContext } from "../Context/InstallationContextProvider"; interface InstallationListProps { searchQuery: string; @@ -36,8 +36,8 @@ const filterData = ( }; const InstallationList = (props: InstallationListProps) => { - const [data, setData] = useState(); - const [loading, setLoading] = useState(false); + const { fetchData, data, loading } = useContext(InstallationContext); + const filteredData = filterData(props.searchQuery, data); const routeMatch = useRouteMatch([ @@ -48,62 +48,56 @@ const InstallationList = (props: InstallationListProps) => { ]); useEffect(() => { - setLoading(true); - axiosConfig.get("/GetAllInstallations", {}).then((res) => { - setData(res.data); - setLoading(false); - }); - }, []); + fetchData(); + }, [fetchData]); - return ( - <> - {loading && ( - - - - )} - {data && ( - - {filteredData?.map((installation) => { - return ( - - + + + ); + } + if (data) { + return ( + + {filteredData?.map((installation) => { + return ( + + + - - - - - - - ); - })} - - )} - - ); + + + + + + ); + })} + + ); + } + return null; }; export default InstallationList; diff --git a/typescript/Frontend/src/components/Installations/Installations.tsx b/typescript/Frontend/src/components/Installations/Installations.tsx index 5a3086918..c78588d83 100644 --- a/typescript/Frontend/src/components/Installations/Installations.tsx +++ b/typescript/Frontend/src/components/Installations/Installations.tsx @@ -5,34 +5,37 @@ import NavigationButtons from "../Layout/NavigationButtons"; import Sidebar from "../Layout/Sidebar"; import BasicTable from "../Layout/Table"; import Alarms from "./Alarms"; -import InstallationDetail from "./Installation"; +import Installation from "./Installation"; import InstallationTabs from "./InstallationTabs"; import Log from "./Log"; import routes from "../../routes.json"; +import InstallationContextProvider from "../Context/InstallationContextProvider"; const Installations = () => { return ( - - - - - + + + + + + + + + + + } + index + /> + } /> + } /> + } /> + + - - - - } - index - /> - } /> - } /> - } /> - - - - + + ); };