diff --git a/typescript/Frontend/package-lock.json b/typescript/Frontend/package-lock.json
index 1bcf739ce..1cdc6cf7e 100644
--- a/typescript/Frontend/package-lock.json
+++ b/typescript/Frontend/package-lock.json
@@ -26,6 +26,7 @@
"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",
@@ -13218,6 +13219,37 @@
"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",
@@ -28150,6 +28182,18 @@
"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 803841d8f..d1fd92422 100644
--- a/typescript/Frontend/package.json
+++ b/typescript/Frontend/package.json
@@ -21,6 +21,7 @@
"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/Login.tsx b/typescript/Frontend/src/Login.tsx
index 6ec794962..6b59b2a10 100644
--- a/typescript/Frontend/src/Login.tsx
+++ b/typescript/Frontend/src/Login.tsx
@@ -1,7 +1,7 @@
import React, { useState } from "react";
import { Alert, Button, CircularProgress, Grid } from "@mui/material";
import Container from "@mui/material/Container";
-import axiosConfig, { axiosConfigWithoutToken } from "./config/axiosConfig";
+import { axiosConfigWithoutToken } from "./config/axiosConfig";
import InnovenergyTextfield from "./components/Layout/InnovenergyTextfield";
const loginUser = async (username: string, password: string) => {
@@ -17,18 +17,20 @@ const Login = ({ setToken }: { setToken: (value: string) => void }) => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
- const verifyToken = async () => {
- axiosConfig.get("/GetAllInstallations");
+ const verifyToken = async (token: string) => {
+ axiosConfigWithoutToken.get("/GetAllInstallations", {
+ headers: { auth: token },
+ });
};
const handleSubmit = () => {
setLoading(true);
loginUser(username, password).then(({ data }) => {
// TODO change this if they return err codes from backend
- if (typeof data === "string") {
- verifyToken()
+ if (data && data.token) {
+ verifyToken(data.token)
.then(() => {
- setToken(data);
+ setToken(data.token);
setLoading(false);
})
.catch((err) => {
diff --git a/typescript/Frontend/src/components/Groups/Folder.tsx b/typescript/Frontend/src/components/Groups/Folder.tsx
index 3215d7564..c767bc533 100644
--- a/typescript/Frontend/src/components/Groups/Folder.tsx
+++ b/typescript/Frontend/src/components/Groups/Folder.tsx
@@ -1,10 +1,11 @@
import { Box, CircularProgress, Alert } from "@mui/material";
import { AxiosError } from "axios";
-import { useState, useEffect } from "react";
+import { useState, useEffect, useContext } 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 2dac7aec6..5611dfc38 100644
--- a/typescript/Frontend/src/components/Groups/FolderForm.tsx
+++ b/typescript/Frontend/src/components/Groups/FolderForm.tsx
@@ -1,9 +1,10 @@
-import { Alert, Button, Grid, Snackbar } from "@mui/material";
+import { Button, CircularProgress, Grid } from "@mui/material";
import { useFormik } from "formik";
import { 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";
interface I_CustomerFormProps {
@@ -19,6 +20,8 @@ const FolderForm = (props: I_CustomerFormProps) => {
const intl = useIntl();
const [snackbarOpen, setSnackbarOpen] = useState(false);
+ const [error, setError] = useState();
+ const [loading, setLoading] = useState(false);
const formik = useFormik({
initialValues: {
@@ -26,21 +29,25 @@ const FolderForm = (props: I_CustomerFormProps) => {
information: values.information,
},
onSubmit: (formikValues) => {
+ setLoading(true);
const idAsNumber = parseInt(id, 10);
updateFolder({
...values,
...formikValues,
id: idAsNumber,
- }).then((res) => {
- setSnackbarOpen(true);
- });
+ })
+ .then((res) => {
+ setSnackbarOpen(true);
+ setLoading(false);
+ })
+ .catch((err) => {
+ setSnackbarOpen(true);
+ setError(err);
+ setLoading(false);
+ });
},
});
- const handleClose = () => {
- setSnackbarOpen(false);
- };
-
return (
);
};
diff --git a/typescript/Frontend/src/components/Groups/GroupTabs.tsx b/typescript/Frontend/src/components/Groups/GroupTabs.tsx
index 06360665c..02e9e72a1 100644
--- a/typescript/Frontend/src/components/Groups/GroupTabs.tsx
+++ b/typescript/Frontend/src/components/Groups/GroupTabs.tsx
@@ -17,45 +17,51 @@ const GroupTabs = () => {
const id = routeMatch?.params?.id;
const intl = useIntl();
- return (
-
-
-
- {routeMatch?.pathname.includes("folder") ? (
-
- ) : (
-
- )}
+ if (id) {
+ return (
+
+
+
+ {routeMatch?.pathname.includes("folder") ? (
+
+ ) : (
+
+ )}
-
-
+
+
+
-
- );
+ );
+ }
+ return null;
};
export default GroupTabs;
diff --git a/typescript/Frontend/src/components/Groups/Groups.tsx b/typescript/Frontend/src/components/Groups/Groups.tsx
index 3fd71db92..71b0392b8 100644
--- a/typescript/Frontend/src/components/Groups/Groups.tsx
+++ b/typescript/Frontend/src/components/Groups/Groups.tsx
@@ -1,7 +1,9 @@
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 NavigationButtons from "../Layout/NavigationButtons";
import Folder from "./Folder";
@@ -9,12 +11,14 @@ import GroupTabs from "./GroupTabs";
import GroupTree from "./Tree/GroupTree";
const Groups = () => {
+ const [data, setData] = useState<(I_Folder | I_Installation)[]>();
+
return (
-
+
diff --git a/typescript/Frontend/src/components/Groups/Tree/DragPreview.tsx b/typescript/Frontend/src/components/Groups/Tree/DragPreview.tsx
index a27d707a9..537c8fa3c 100644
--- a/typescript/Frontend/src/components/Groups/Tree/DragPreview.tsx
+++ b/typescript/Frontend/src/components/Groups/Tree/DragPreview.tsx
@@ -1,7 +1,7 @@
import { DragLayerMonitorProps } from "@minoru/react-dnd-treeview";
import { I_Installation, I_Folder } from "../../../util/types";
import styles from "./DragPreview.module.scss";
-import TypeIcon from "./TypeIcon";
+import TypeIcon from "../TypeIcon";
interface DragPreviewProps {
monitorProps: DragLayerMonitorProps;
diff --git a/typescript/Frontend/src/components/Groups/Tree/GroupDataContext.tsx b/typescript/Frontend/src/components/Groups/Tree/GroupDataContext.tsx
new file mode 100644
index 000000000..9134d4c9a
--- /dev/null
+++ b/typescript/Frontend/src/components/Groups/Tree/GroupDataContext.tsx
@@ -0,0 +1,13 @@
+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.module.scss b/typescript/Frontend/src/components/Groups/Tree/GroupTree.module.scss
index 11d791fb0..be13fe81b 100644
--- a/typescript/Frontend/src/components/Groups/Tree/GroupTree.module.scss
+++ b/typescript/Frontend/src/components/Groups/Tree/GroupTree.module.scss
@@ -20,6 +20,6 @@
}
.treeContainer {
- height: 500px;
+ max-height: 500px;
overflow: auto;
}
diff --git a/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx b/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx
index c5b9ffe9d..df8868f10 100644
--- a/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx
+++ b/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx
@@ -1,7 +1,7 @@
-import { useEffect, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
import axiosConfig from "../../../config/axiosConfig";
import { I_Folder, I_Installation } from "../../../util/types";
-import { CircularProgress, Grid } from "@mui/material";
+import { Alert, CircularProgress, Grid } from "@mui/material";
import { DndProvider } from "react-dnd";
import {
MultiBackend,
@@ -14,6 +14,7 @@ import TreeNode from "./TreeNode";
import styles from "./GroupTree.module.scss";
import withScrolling from "react-dnd-scrolling";
import DragPreview from "./DragPreview";
+import InnovenergySnackbar from "../../InnovenergySnackbar";
const getTreeData = (
data: (I_Folder | I_Installation)[]
@@ -30,21 +31,49 @@ const getTreeData = (
});
};
-const GroupTree = () => {
- const [data, setData] = useState<(I_Folder | I_Installation)[]>();
+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 [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]);
- const getData = async () => {
- setLoading(true);
- return axiosConfig.get("/GetAllFoldersAndInstallations").then((res) => {
- setData(res.data);
- setLoading(false);
- });
+ const findParent = (element: I_Folder | I_Installation): any => {
+ if (data) {
+ const parent = data.find(
+ (el) => el.type === "Folder" && el.id === element.parentId
+ );
+ if (parent) {
+ return findParent(parent);
+ }
+ return element.parentId;
+ }
};
const handleDrop = (
@@ -62,7 +91,13 @@ const GroupTree = () => {
}
)
.then(() => {
+ setSnackbarOpen(true);
getData();
+ })
+ .catch((err) => {
+ setPutError(err);
+ setLoading(false);
+ setSnackbarOpen(true);
});
};
@@ -107,8 +142,19 @@ const GroupTree = () => {
) => handleDrop(tree, options)}
/>
+
);
+ } else if (getError) {
+ return (
+
+ Couldn't load data
+
+ );
}
return null;
};
diff --git a/typescript/Frontend/src/components/Groups/Tree/TreeNode.tsx b/typescript/Frontend/src/components/Groups/Tree/TreeNode.tsx
index 3080cab71..5047e07ea 100644
--- a/typescript/Frontend/src/components/Groups/Tree/TreeNode.tsx
+++ b/typescript/Frontend/src/components/Groups/Tree/TreeNode.tsx
@@ -6,7 +6,7 @@ import styles from "./TreeNode.module.scss";
import { Link } from "react-router-dom";
import routes from "../../../routes.json";
import { I_Folder, I_Installation } from "../../../util/types";
-import TypeIcon from "./TypeIcon";
+import TypeIcon from "../TypeIcon";
import DragHandleIcon from "@mui/icons-material/DragHandle";
type Props = {
@@ -52,6 +52,7 @@ const TreeNode: React.FC = (props) => {
textDecoration: "none",
color: "black",
}}
+ draggable={false}
>
{node.text}
diff --git a/typescript/Frontend/src/components/Groups/Tree/TypeIcon.tsx b/typescript/Frontend/src/components/Groups/TypeIcon.tsx
similarity index 100%
rename from typescript/Frontend/src/components/Groups/Tree/TypeIcon.tsx
rename to typescript/Frontend/src/components/Groups/TypeIcon.tsx
diff --git a/typescript/Frontend/src/components/InnovenergySnackbar.tsx b/typescript/Frontend/src/components/InnovenergySnackbar.tsx
new file mode 100644
index 000000000..5223131b9
--- /dev/null
+++ b/typescript/Frontend/src/components/InnovenergySnackbar.tsx
@@ -0,0 +1,46 @@
+import { Alert, Snackbar } from "@mui/material";
+import { FormattedMessage } from "react-intl";
+
+interface InnovenergySnackbarProps {
+ open: boolean;
+ setOpen: (value: boolean) => void;
+ error?: any;
+}
+const InnovenergySnackbar = (props: InnovenergySnackbarProps) => {
+ const { open, setOpen, error } = props;
+
+ const handleClose = () => {
+ setOpen(false);
+ };
+ return (
+
+
+ {error ? (
+
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export default InnovenergySnackbar;
diff --git a/typescript/Frontend/src/lang/de.json b/typescript/Frontend/src/lang/de.json
index 80d807f0f..037eeab43 100644
--- a/typescript/Frontend/src/lang/de.json
+++ b/typescript/Frontend/src/lang/de.json
@@ -16,6 +16,7 @@
"logout": "Logout",
"updatedSuccessfully": "Erfolgreich aktualisiert",
"groups": "Gruppen",
- "group": "Gruppe",
- "folder": "Ordner"
+ "group": "Gruppe",
+ "folder": "Ordner",
+ "updateFolderErrorMessage": "Der Ordner konnte nicht aktualisiert werden, ein Fehler ist aufgetreten"
}
diff --git a/typescript/Frontend/src/lang/en.json b/typescript/Frontend/src/lang/en.json
index 856e907aa..45abb09c2 100644
--- a/typescript/Frontend/src/lang/en.json
+++ b/typescript/Frontend/src/lang/en.json
@@ -17,6 +17,6 @@
"updatedSuccessfully": "Updated successfully",
"groups": "Groups",
"group": "Group",
- "folder": "folder"
-
+ "folder": "folder",
+ "updateFolderErrorMessage": "Couldn't update folder, an error occured"
}