From 30c9752f9a3965108992ec7e5df302fc5988ec15 Mon Sep 17 00:00:00 2001 From: Sina Blattmann Date: Mon, 3 Apr 2023 15:55:50 +0200 Subject: [PATCH] Fix a few small bugs, revert Detail Component --- typescript/Frontend/src/App.tsx | 3 +- typescript/Frontend/src/Login.tsx | 7 +- ...tProvider.tsx => UsersContextProvider.tsx} | 18 ++--- .../AccessManagement/AccessManagement.tsx | 6 +- .../AccessManagement/AvailableUserDialog.tsx | 19 +++--- .../UsersWithDirectAccess.tsx | 4 +- .../UsersWithInheritedAccess.tsx | 4 +- .../src/components/Groups/AddNewDialog.tsx | 40 ++++++++--- .../src/components/Groups/FolderForm.tsx | 12 ++-- .../src/components/Groups/GroupTabs.tsx | 23 +++++-- .../Frontend/src/components/Groups/Groups.tsx | 13 +--- .../src/components/Groups/LocationForm.tsx | 12 ++-- .../src/components/Groups/Tree/GroupTree.tsx | 18 ++--- .../src/components/Groups/Tree/MoveDialog.tsx | 11 ++-- .../components/Installations/Installation.tsx | 60 +++++++++++++++++ .../Installations/InstallationForm.tsx | 17 +++-- .../Installations/InstallationList.tsx | 7 +- .../Installations/InstallationTabs.tsx | 7 +- .../Installations/Installations.tsx | 18 ++--- .../src/components/Installations/Log.tsx | 1 - .../Frontend/src/components/Layout/Detail.tsx | 8 ++- .../components/Layout/InnovenergyButton.tsx | 24 +++++++ .../src/components/Layout/LogoutButton.tsx | 7 +- .../src/components/Layout/ModeButtons.tsx | 10 +-- .../components/Layout/NavigationButtons.tsx | 66 +++++++++++++------ .../Layout/{Sidebar.tsx => Search.tsx} | 14 ++-- .../Frontend/src/components/Users/AddUser.tsx | 61 +++++++++++++++++ .../Frontend/src/components/Users/User.tsx | 59 +++++++++++++++++ .../src/components/Users/UserForm.tsx | 30 ++++----- .../src/components/Users/UserList.tsx | 28 ++++---- .../src/components/Users/UserTabs.tsx | 11 ++-- .../Frontend/src/components/Users/Users.tsx | 29 +++----- typescript/Frontend/src/lang/en.json | 2 +- .../Frontend/src/util/installation.util.tsx | 4 +- typescript/Frontend/src/util/types.tsx | 9 +-- 35 files changed, 447 insertions(+), 215 deletions(-) rename typescript/Frontend/src/components/Context/{UserContextProvider.tsx => UsersContextProvider.tsx} (87%) create mode 100644 typescript/Frontend/src/components/Installations/Installation.tsx create mode 100644 typescript/Frontend/src/components/Layout/InnovenergyButton.tsx rename typescript/Frontend/src/components/Layout/{Sidebar.tsx => Search.tsx} (61%) create mode 100644 typescript/Frontend/src/components/Users/AddUser.tsx create mode 100644 typescript/Frontend/src/components/Users/User.tsx diff --git a/typescript/Frontend/src/App.tsx b/typescript/Frontend/src/App.tsx index fc1a9108c..f4ba7f761 100644 --- a/typescript/Frontend/src/App.tsx +++ b/typescript/Frontend/src/App.tsx @@ -7,7 +7,6 @@ import { IntlProvider } from "react-intl"; import { useState } from "react"; import en from "./lang/en.json"; import de from "./lang/de.json"; -import Installations from "./components/Installations/Installations"; import LanguageSelect from "./components/Layout/LanguageSelect"; import LogoutButton from "./components/Layout/LogoutButton"; import Users from "./components/Users/Users"; @@ -36,7 +35,7 @@ const App = () => { locale={language} defaultLocale="EN" > - + diff --git a/typescript/Frontend/src/Login.tsx b/typescript/Frontend/src/Login.tsx index 9bafc45d3..4b144a28c 100644 --- a/typescript/Frontend/src/Login.tsx +++ b/typescript/Frontend/src/Login.tsx @@ -1,8 +1,9 @@ import React, { useState } from "react"; -import { Alert, Button, CircularProgress, Grid } from "@mui/material"; +import { Alert, CircularProgress, Grid } from "@mui/material"; import Container from "@mui/material/Container"; import { axiosConfigWithoutToken } from "./config/axiosConfig"; import InnovenergyTextfield from "./components/Layout/InnovenergyTextfield"; +import InnovenergyButton from "./components/Layout/InnovenergyButton"; const loginUser = async (username: string, password: string) => { return axiosConfigWithoutToken.post("/Login", null, { @@ -67,9 +68,9 @@ const Login = ({ setToken, setLanguage }: I_LoginProps) => { /> {error && Incorrect username or password} - + {loading && } diff --git a/typescript/Frontend/src/components/Context/UserContextProvider.tsx b/typescript/Frontend/src/components/Context/UsersContextProvider.tsx similarity index 87% rename from typescript/Frontend/src/components/Context/UserContextProvider.tsx rename to typescript/Frontend/src/components/Context/UsersContextProvider.tsx index 05fd19712..d6150b0e3 100644 --- a/typescript/Frontend/src/components/Context/UserContextProvider.tsx +++ b/typescript/Frontend/src/components/Context/UsersContextProvider.tsx @@ -11,26 +11,26 @@ import axiosConfig from "../../config/axiosConfig"; import { I_User, UserWithInheritedAccess } from "../../util/user.util"; import { GroupContext } from "./GroupContextProvider"; -interface UserContextProviderProps { +interface UsersContextProviderProps { directAccessUsers: I_User[]; setDirectAccessUsers: (value: I_User[]) => void; inheritedAccessUsers: UserWithInheritedAccess[]; setInheritedAccessUsers: (value: UserWithInheritedAccess[]) => void; availableUsers: I_User[]; setAvailableUsers: (value: I_User[]) => void; - getAvailableUsers: () => I_User[]; + getAvailableUsersForResource: () => I_User[]; fetchUsersWithInheritedAccessForResource: () => Promise; fetchUsersWithDirectAccessForResource: () => Promise; } -export const UserContext = createContext({ +export const UsersContext = createContext({ directAccessUsers: [], setDirectAccessUsers: () => {}, inheritedAccessUsers: [], setInheritedAccessUsers: () => {}, availableUsers: [], setAvailableUsers: () => {}, - getAvailableUsers: () => { + getAvailableUsersForResource: () => { return []; }, fetchUsersWithInheritedAccessForResource: () => { @@ -41,7 +41,7 @@ export const UserContext = createContext({ }, }); -const UserContextProvider = ({ children }: { children: ReactNode }) => { +const UsersContextProvider = ({ children }: { children: ReactNode }) => { const [directAccessUsers, setDirectAccessUsers] = useState([]); const [inheritedAccessUsers, setInheritedAccessUsers] = useState< UserWithInheritedAccess[] @@ -88,7 +88,7 @@ const UserContextProvider = ({ children }: { children: ReactNode }) => { }, []); return ( - { setInheritedAccessUsers, availableUsers, setAvailableUsers, - getAvailableUsers: getAvailableUsersForResource, + getAvailableUsersForResource, fetchUsersWithInheritedAccessForResource, fetchUsersWithDirectAccessForResource, }} > {children} - + ); }; -export default UserContextProvider; +export default UsersContextProvider; diff --git a/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx b/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx index c35e5d0a9..d828e0922 100644 --- a/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx +++ b/typescript/Frontend/src/components/Groups/AccessManagement/AccessManagement.tsx @@ -1,5 +1,5 @@ import { Grid } from "@mui/material"; -import UserContextProvider from "../../Context/UserContextProvider"; +import UsersContextProvider from "../../Context/UsersContextProvider"; import AvailableUserDialog from "./AvailableUserDialog"; import InnovenergyList from "./InnovenergyList"; import UsersWithDirectAccess from "./UsersWithDirectAccess"; @@ -7,7 +7,7 @@ import UsersWithInheritedAccess from "./UsersWithInheritedAccess"; const AccessManagement = () => { return ( - + @@ -17,7 +17,7 @@ const AccessManagement = () => { - + ); }; diff --git a/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx b/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx index 5d46d6bd1..871f1932c 100644 --- a/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx +++ b/typescript/Frontend/src/components/Groups/AccessManagement/AvailableUserDialog.tsx @@ -1,6 +1,5 @@ import { Autocomplete, - Button, Dialog, DialogContent, DialogTitle, @@ -13,7 +12,8 @@ import { useParams } from "react-router-dom"; import axiosConfig from "../../../config/axiosConfig"; import { I_User } from "../../../util/user.util"; import { GroupContext } from "../../Context/GroupContextProvider"; -import { UserContext } from "../../Context/UserContextProvider"; +import { UsersContext } from "../../Context/UsersContextProvider"; +import InnovenergyButton from "../../Layout/InnovenergyButton"; const AvailableUserDialog = () => { const [selectedUsers, setSelectedUsers] = useState([]); @@ -22,10 +22,10 @@ const AvailableUserDialog = () => { const { currentType } = useContext(GroupContext); const { id } = useParams(); const { - getAvailableUsers, + getAvailableUsersForResource, fetchUsersWithDirectAccessForResource, fetchUsersWithInheritedAccessForResource, - } = useContext(UserContext); + } = useContext(UsersContext); const handleGrant = () => { selectedUsers.forEach((user) => { @@ -63,7 +63,7 @@ const AvailableUserDialog = () => { sx={{ width: "500px" }} multiple id="tags-standard" - options={getAvailableUsers()} + options={getAvailableUsersForResource()} getOptionLabel={(option) => option.name} renderInput={(params) => ( { /> - + - + ); }; diff --git a/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx b/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx index 14383a4b8..ee52dc624 100644 --- a/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx +++ b/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithDirectAccess.tsx @@ -10,13 +10,13 @@ import { Fragment, useContext, useEffect } from "react"; import axiosConfig from "../../../config/axiosConfig"; import PersonRemoveIcon from "@mui/icons-material/PersonRemove"; import PersonIcon from "@mui/icons-material/Person"; -import { UserContext } from "../../Context/UserContextProvider"; +import { UsersContext } from "../../Context/UsersContextProvider"; import { useParams } from "react-router-dom"; import { GroupContext } from "../../Context/GroupContextProvider"; const UsersWithDirectAccess = () => { const { fetchUsersWithDirectAccessForResource, directAccessUsers } = - useContext(UserContext); + useContext(UsersContext); const { currentType } = useContext(GroupContext); const { id } = useParams(); diff --git a/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithInheritedAccess.tsx b/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithInheritedAccess.tsx index 5596dd83b..60f20865f 100644 --- a/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithInheritedAccess.tsx +++ b/typescript/Frontend/src/components/Groups/AccessManagement/UsersWithInheritedAccess.tsx @@ -13,11 +13,11 @@ import { } from "../../../util/user.util"; import routes from "../../../routes.json"; import PersonIcon from "@mui/icons-material/Person"; -import { UserContext } from "../../Context/UserContextProvider"; +import { UsersContext } from "../../Context/UsersContextProvider"; const UsersWithInheritedAccess = () => { const { fetchUsersWithInheritedAccessForResource, inheritedAccessUsers } = - useContext(UserContext); + useContext(UsersContext); useEffect(() => { fetchUsersWithInheritedAccessForResource(); diff --git a/typescript/Frontend/src/components/Groups/AddNewDialog.tsx b/typescript/Frontend/src/components/Groups/AddNewDialog.tsx index 6af3b2465..074251907 100644 --- a/typescript/Frontend/src/components/Groups/AddNewDialog.tsx +++ b/typescript/Frontend/src/components/Groups/AddNewDialog.tsx @@ -1,9 +1,11 @@ -import { Button, Dialog, DialogContent, DialogTitle } from "@mui/material"; +import { Dialog, DialogContent, DialogTitle, IconButton } from "@mui/material"; import { useState } from "react"; import { FormattedMessage } from "react-intl"; import axiosConfig from "../../config/axiosConfig"; import { I_Folder } from "../../util/types"; import FolderForm from "./FolderForm"; +import CloseIcon from "@mui/icons-material/Close"; +import InnovenergyButton from "../Layout/InnovenergyButton"; interface AddNewDialogProps { values: I_Folder; @@ -13,17 +15,22 @@ const AddNewDialog = (props: AddNewDialogProps) => { const [open, setOpen] = useState(false); const handleSubmit = (data: I_Folder, childData: Partial) => { - return axiosConfig.post("/CreateFolder", { - ...childData, - parentId: data.id, - }); + return axiosConfig + .post("/CreateFolder", { + ...childData, + parentId: data.id, + }) + .then((res) => { + setOpen(false); + return res; + }); }; return ( <> - + setOpen(false)} aria-labelledby="customized-dialog-title" @@ -33,8 +40,25 @@ const AddNewDialog = (props: AddNewDialogProps) => { maxHeight: 500, }} scroll="paper" + fullWidth + maxWidth="sm" > - Create new folder + + Create new folder + setOpen(false)} + aria-label="close" + sx={{ + position: "absolute", + right: 8, + top: 8, + }} + > + + + diff --git a/typescript/Frontend/src/components/Groups/FolderForm.tsx b/typescript/Frontend/src/components/Groups/FolderForm.tsx index 6d612b425..fa69fcc17 100644 --- a/typescript/Frontend/src/components/Groups/FolderForm.tsx +++ b/typescript/Frontend/src/components/Groups/FolderForm.tsx @@ -1,4 +1,4 @@ -import { Button, CircularProgress, Grid } from "@mui/material"; +import { CircularProgress, Grid } from "@mui/material"; import { AxiosResponse } from "axios"; import { useFormik } from "formik"; import { ReactNode, useContext, useState } from "react"; @@ -6,6 +6,7 @@ import { FormattedMessage, useIntl } from "react-intl"; import { I_Folder } from "../../util/types"; import { GroupContext } from "../Context/GroupContextProvider"; import InnovenergySnackbar from "../InnovenergySnackbar"; +import InnovenergyButton from "../Layout/InnovenergyButton"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; interface I_CustomerFormProps { @@ -28,8 +29,9 @@ const FolderForm = (props: I_CustomerFormProps) => { const formik = useFormik({ initialValues: { - name: values.name, - information: values.information, + name: additionalButtons && additionalButtons.length ? values.name : "", + information: + additionalButtons && additionalButtons.length ? values.information : "", }, onSubmit: (formikValues) => { setLoading(true); @@ -73,9 +75,9 @@ const FolderForm = (props: I_CustomerFormProps) => { {loading && } {additionalButtons && additionalButtons.map((button) => button)} - + { const routeMatch = useRouteMatch([ - routes.groups + routes.folder + ":id", - routes.groups + routes.manageAccess + ":id", - routes.groups + routes.installation + ":id", + routes.installations + routes.tree + routes.folder + ":id", + routes.installations + routes.tree + routes.manageAccess + ":id", + routes.installations + routes.tree + routes.installation + ":id", ]); const id = routeMatch?.params?.id; const intl = useIntl(); const { currentType } = useContext(GroupContext); + console.log(routeMatch); + if (id) { return ( @@ -32,7 +34,9 @@ const GroupTabs = () => { id: "folder", defaultMessage: "Folder", })} - value={routes.groups + routes.folder + ":id"} + value={ + routes.installations + routes.tree + routes.folder + ":id" + } component={Link} to={routes.folder + id} /> @@ -42,7 +46,12 @@ const GroupTabs = () => { id: "installation", defaultMessage: "Installation", })} - value={routes.groups + routes.installation + ":id"} + value={ + routes.installations + + routes.tree + + routes.installation + + ":id" + } component={Link} to={routes.installation + id} /> @@ -53,7 +62,9 @@ const GroupTabs = () => { id: "manageAccess", defaultMessage: "Manage access", })} - value={routes.groups + routes.manageAccess + ":id"} + value={ + routes.installations + routes.tree + routes.manageAccess + ":id" + } component={Link} to={routes.manageAccess + id} /> diff --git a/typescript/Frontend/src/components/Groups/Groups.tsx b/typescript/Frontend/src/components/Groups/Groups.tsx index b715275c2..9c56e56fd 100644 --- a/typescript/Frontend/src/components/Groups/Groups.tsx +++ b/typescript/Frontend/src/components/Groups/Groups.tsx @@ -1,16 +1,12 @@ import { Grid } from "@mui/material"; -import { Container } from "@mui/system"; import { Routes, Route } from "react-router"; import routes from "../../routes.json"; import Folder from "./Folder"; import GroupTabs from "./GroupTabs"; import GroupContextProvider from "../Context/GroupContextProvider"; import GroupTree from "./Tree/GroupTree"; -import NavigationButtons from "../Layout/NavigationButtons"; import AccessManagement from "./AccessManagement/AccessManagement"; -import { I_Installation } from "../../util/types"; -import InstallationForm from "../Installations/InstallationForm"; -import Detail from "../Layout/Detail"; +import Installation from "../Installations/Installation"; const Groups = () => { return ( @@ -29,12 +25,7 @@ const Groups = () => { /> - route="/GetInstallationById?id=" - formComponent={InstallationForm} - /> - } + element={} /> diff --git a/typescript/Frontend/src/components/Groups/LocationForm.tsx b/typescript/Frontend/src/components/Groups/LocationForm.tsx index f593da12e..54a2a4e5b 100644 --- a/typescript/Frontend/src/components/Groups/LocationForm.tsx +++ b/typescript/Frontend/src/components/Groups/LocationForm.tsx @@ -1,10 +1,10 @@ -import { Button, Grid, InputLabel } from "@mui/material"; +import { Grid, InputLabel } from "@mui/material"; import { useContext, useState } from "react"; import { FormattedMessage } from "react-intl"; import { useParams } from "react-router-dom"; import axiosConfig from "../../config/axiosConfig"; import { GroupContext } from "../Context/GroupContextProvider"; -import MoveTree from "./Tree/MoveTree"; +import InnovenergyButton from "../Layout/InnovenergyButton"; interface LocationFormProps { parentId: number; @@ -39,13 +39,9 @@ const LocationForm = (props: LocationFormProps) => { - + ); diff --git a/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx b/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx index 2731dbd8c..12a2346f6 100644 --- a/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx +++ b/typescript/Frontend/src/components/Groups/Tree/GroupTree.tsx @@ -19,14 +19,6 @@ const GroupTree = () => { fetchData(); }, [fetchData]); - /* useEffect(() => { - if (id) { - setSelected( - currentType === "Folder" ? "Folder" + id : "Installation" + id - ); - } - }, [id, currentType]); */ - const getNodes = (element: I_Folder | I_Installation): null | ReactNode => { if (instanceOfFolder(element)) { return element.children ? renderTree(element.children) : null; @@ -55,6 +47,9 @@ const GroupTree = () => { nodeId={element.type + element.id} label={element.name} onClick={() => setCurrentType(element.type)} + sx={{ + ".MuiTreeItem-content": { paddingY: "12px" }, + }} > {getNodes(element)} @@ -75,7 +70,12 @@ const GroupTree = () => { aria-label="rich object" defaultCollapseIcon={} defaultExpandIcon={} - sx={{ height: 300, flexGrow: 1, maxWidth: 400 }} + sx={{ + height: 480, + flexGrow: 1, + overflow: "auto", + ".MuiTreeView-root": { overflowX: "hidden" }, + }} expanded={openNodes} onNodeToggle={(e, ids) => { setOpenNodes(ids); diff --git a/typescript/Frontend/src/components/Groups/Tree/MoveDialog.tsx b/typescript/Frontend/src/components/Groups/Tree/MoveDialog.tsx index 6f810989c..8da47d7ec 100644 --- a/typescript/Frontend/src/components/Groups/Tree/MoveDialog.tsx +++ b/typescript/Frontend/src/components/Groups/Tree/MoveDialog.tsx @@ -1,4 +1,4 @@ -import { Button, DialogActions, Dialog, DialogTitle } from "@mui/material"; +import { DialogActions, Dialog, DialogTitle } from "@mui/material"; import DialogContent from "@mui/material/DialogContent"; import { useContext, useState } from "react"; import { FormattedMessage } from "react-intl"; @@ -6,6 +6,7 @@ import { useParams } from "react-router-dom"; import axiosConfig from "../../../config/axiosConfig"; import { I_Folder } from "../../../util/types"; import { GroupContext } from "../../Context/GroupContextProvider"; +import InnovenergyButton from "../../Layout/InnovenergyButton"; import MoveTree from "./MoveTree"; interface MoveDialogProps { @@ -37,9 +38,9 @@ const MoveDialog = (props: MoveDialogProps) => { return (
- + setOpen(false)} aria-labelledby="customized-dialog-title" @@ -58,9 +59,7 @@ const MoveDialog = (props: MoveDialogProps) => { /> - + Move
diff --git a/typescript/Frontend/src/components/Installations/Installation.tsx b/typescript/Frontend/src/components/Installations/Installation.tsx new file mode 100644 index 000000000..a73fef895 --- /dev/null +++ b/typescript/Frontend/src/components/Installations/Installation.tsx @@ -0,0 +1,60 @@ +import { Alert, Box, CircularProgress } from "@mui/material"; +import { AxiosError } from "axios"; +import { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import axiosConfig from "../../config/axiosConfig"; +import { I_Installation } from "../../util/types"; +import InstallationForm from "./InstallationForm"; + +interface I_InstallationProps { + hasMoveButton?: boolean; +} +const Installation = (props: I_InstallationProps) => { + const { id } = useParams(); + const [values, setValues] = useState(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(); + + useEffect(() => { + setLoading(true); + axiosConfig + .get("/GetInstallationById?id=" + id) + .then((res) => { + setValues(res.data); + setLoading(false); + }) + .catch((err: AxiosError) => { + setError(err); + setLoading(false); + }); + }, [id]); + + if (values && values.id && values.id.toString() === id) { + return ( + + + + ); + } else if (loading) { + return ( + + + + ); + } else if (error) { + return ( + + {error.message} + + ); + } + return null; +}; + +export default Installation; diff --git a/typescript/Frontend/src/components/Installations/InstallationForm.tsx b/typescript/Frontend/src/components/Installations/InstallationForm.tsx index 638f9bdd7..8966918f1 100644 --- a/typescript/Frontend/src/components/Installations/InstallationForm.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationForm.tsx @@ -1,21 +1,25 @@ -import { Alert, Button, Grid, Snackbar } from "@mui/material"; +import { Alert, Grid, Snackbar } 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_Installation } from "../../util/types"; +import { InstallationContext } from "../Context/InstallationContextProvider"; import MoveDialog from "../Groups/Tree/MoveDialog"; +import InnovenergyButton from "../Layout/InnovenergyButton"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; interface I_InstallationFormProps { values: I_Installation; id: string | undefined; + hasMoveButton?: boolean; } const InstallationForm = (props: I_InstallationFormProps) => { - const { values, id } = props; + const { values, id, hasMoveButton } = props; const [open, setOpen] = useState(false); const intl = useIntl(); + const { fetchData } = useContext(InstallationContext); const formik = useFormik({ initialValues: { @@ -33,6 +37,7 @@ const InstallationForm = (props: I_InstallationFormProps) => { }) .then(() => { setOpen(true); + fetchData(); }); }, }); @@ -94,10 +99,10 @@ const InstallationForm = (props: I_InstallationFormProps) => { handleChange={formik.handleChange} /> - - + { const filteredData = filterData(props.searchQuery, data); const routeMatch = useRouteMatch([ - routes.installations + routes.installation + ":id", - routes.installations + routes.alarms + ":id", - routes.installations + routes.log + ":id", + routes.installations + routes.list + routes.installation + ":id", + routes.installations + routes.list + routes.alarms + ":id", + routes.installations + routes.list + routes.log + ":id", ]); useEffect(() => { @@ -95,7 +95,6 @@ const InstallationList = (props: InstallationListProps) => { ); } else if (error) { - console.log(error); return ( {error.message} diff --git a/typescript/Frontend/src/components/Installations/InstallationTabs.tsx b/typescript/Frontend/src/components/Installations/InstallationTabs.tsx index 273173287..0e2cfaf62 100644 --- a/typescript/Frontend/src/components/Installations/InstallationTabs.tsx +++ b/typescript/Frontend/src/components/Installations/InstallationTabs.tsx @@ -1,12 +1,9 @@ import * as React from "react"; -import Tabs from "@mui/material/Tabs"; -import Tab from "@mui/material/Tab"; import Box from "@mui/material/Box"; -import { Link, Routes } from "react-router-dom"; +import { Link } from "react-router-dom"; import routes from "../../routes.json"; import useRouteMatch from "../../hooks/useRouteMatch"; import { useIntl } from "react-intl"; -import { styled } from "@mui/material"; import { AntTabs, StyledTab } from "../../util/installation.util"; const InstallationTabs = () => { @@ -25,7 +22,7 @@ const InstallationTabs = () => { { return ( - + - route="/GetInstallationById?id=" - formComponent={InstallationForm} - /> - } + element={} index /> } /> diff --git a/typescript/Frontend/src/components/Installations/Log.tsx b/typescript/Frontend/src/components/Installations/Log.tsx index c3606b476..b3b288e40 100644 --- a/typescript/Frontend/src/components/Installations/Log.tsx +++ b/typescript/Frontend/src/components/Installations/Log.tsx @@ -149,7 +149,6 @@ const Log = () => { return previous; }, {}); - console.log(flattenObject(foo.Devices[0])); return
log
; }; diff --git a/typescript/Frontend/src/components/Layout/Detail.tsx b/typescript/Frontend/src/components/Layout/Detail.tsx index 33823cca1..d36e0515e 100644 --- a/typescript/Frontend/src/components/Layout/Detail.tsx +++ b/typescript/Frontend/src/components/Layout/Detail.tsx @@ -6,14 +6,16 @@ import axiosConfig from "../../config/axiosConfig"; export interface I_FormProps { values: T; id: string; + hasMoveButton?: boolean; } interface I_DetailProps { formComponent: FC>; route: string; + hasMoveButton?: boolean; } const Detail = (props: I_DetailProps) => { const { id } = useParams(); - const { formComponent: FormComponent, route } = props; + const { formComponent: FormComponent, route, hasMoveButton } = props; const [values, setValues] = useState(); const [loading, setLoading] = useState(false); const [error, setError] = useState(); @@ -33,7 +35,9 @@ const Detail = (props: I_DetailProps) => { }, [id, route]); if (values && values.id && values.id.toString() === id) { - return ; + return ( + + ); } else if (loading) { return ( ; + sx?: SxProps | undefined; +} + +const InnovenergyButton = (props: I_InnovenergyButtonProps) => { + return ( + + ); +}; + +export default InnovenergyButton; diff --git a/typescript/Frontend/src/components/Layout/LogoutButton.tsx b/typescript/Frontend/src/components/Layout/LogoutButton.tsx index bb1ab45ad..f6dac16d0 100644 --- a/typescript/Frontend/src/components/Layout/LogoutButton.tsx +++ b/typescript/Frontend/src/components/Layout/LogoutButton.tsx @@ -1,7 +1,7 @@ -import { Button } from "@mui/material"; import { FormattedMessage } from "react-intl"; import { useNavigate } from "react-router-dom"; import axiosConfig from "../../config/axiosConfig"; +import InnovenergyButton from "./InnovenergyButton"; interface LogoutButtonProps { removeToken: () => void; @@ -10,8 +10,7 @@ const LogoutButton = (props: LogoutButtonProps) => { const navigate = useNavigate(); return ( - + ); }; diff --git a/typescript/Frontend/src/components/Layout/ModeButtons.tsx b/typescript/Frontend/src/components/Layout/ModeButtons.tsx index 71fe2b925..01158b8ce 100644 --- a/typescript/Frontend/src/components/Layout/ModeButtons.tsx +++ b/typescript/Frontend/src/components/Layout/ModeButtons.tsx @@ -3,7 +3,7 @@ import useRouteMatch from "../../hooks/useRouteMatch"; import routes from "../../routes.json"; import ListIcon from "@mui/icons-material/List"; import AccountTreeIcon from "@mui/icons-material/AccountTree"; -import { AntTabs, StyledTab } from "../../util/installation.util"; +import { ToggleButtonGroup, ToggleButton } from "@mui/material"; const ModeButtons = () => { const routeMatch = useRouteMatch([ @@ -13,7 +13,7 @@ const ModeButtons = () => { return ( <> - @@ -29,8 +29,8 @@ const ModeButtons = () => { component={Link} to={routes.tree} /> - - {/* */} + { > - */} + ); }; diff --git a/typescript/Frontend/src/components/Layout/NavigationButtons.tsx b/typescript/Frontend/src/components/Layout/NavigationButtons.tsx index 1c309705f..e9033cc84 100644 --- a/typescript/Frontend/src/components/Layout/NavigationButtons.tsx +++ b/typescript/Frontend/src/components/Layout/NavigationButtons.tsx @@ -1,8 +1,8 @@ -import { ToggleButton, ToggleButtonGroup } from "@mui/material"; import { FormattedMessage } from "react-intl"; import { Link } from "react-router-dom"; import useRouteMatch from "../../hooks/useRouteMatch"; import routes from "../../routes.json"; +import { AntTabs, StyledTab } from "../../util/installation.util"; const NavigationButtons = () => { const routeMatch = useRouteMatch([ @@ -11,28 +11,52 @@ const NavigationButtons = () => { ]); return ( - - + {/* - - - + + + + + + */} + - - - + + } + /> + } + /> + + ); }; diff --git a/typescript/Frontend/src/components/Layout/Sidebar.tsx b/typescript/Frontend/src/components/Layout/Search.tsx similarity index 61% rename from typescript/Frontend/src/components/Layout/Sidebar.tsx rename to typescript/Frontend/src/components/Layout/Search.tsx index 5b19d2462..400f92d03 100644 --- a/typescript/Frontend/src/components/Layout/Sidebar.tsx +++ b/typescript/Frontend/src/components/Layout/Search.tsx @@ -1,9 +1,12 @@ import { TextField } from "@mui/material"; -import { useState } from "react"; +import { FC, useState } from "react"; import { useIntl } from "react-intl"; -import InstallationList from "../Installations/InstallationList"; -const Sidebar = () => { +interface SearchSidebarProps { + listComponent: FC<{ searchQuery: string }>; +} +const SearchSidebar = (props: SearchSidebarProps) => { + const { listComponent: ListComponent } = props; const [searchQuery, setSearchQuery] = useState(""); const intl = useIntl(); @@ -20,10 +23,9 @@ const Sidebar = () => { value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} /> - - + ); }; -export default Sidebar; +export default SearchSidebar; diff --git a/typescript/Frontend/src/components/Users/AddUser.tsx b/typescript/Frontend/src/components/Users/AddUser.tsx new file mode 100644 index 000000000..de7182310 --- /dev/null +++ b/typescript/Frontend/src/components/Users/AddUser.tsx @@ -0,0 +1,61 @@ +import { Dialog, DialogTitle, IconButton, DialogContent } from "@mui/material"; +import { useState } from "react"; +import { FormattedMessage } from "react-intl"; +import axiosConfig from "../../config/axiosConfig"; +import { I_User } from "../../util/user.util"; +import InnovenergyButton from "../Layout/InnovenergyButton"; +import CloseIcon from "@mui/icons-material/Close"; +import UserForm from "./UserForm"; + +const AddUser = () => { + const [open, setOpen] = useState(false); + + const handleSubmit = (data: Partial) => { + return axiosConfig.post("/CreateUser", data).then((res) => { + setOpen(false); + return res; + }); + }; + + return ( + <> + setOpen(true)}> + + + setOpen(false)} + aria-labelledby="customized-dialog-title" + open={open} + sx={{ + ".MuiDialogContent-root": { overflowX: "hidden" }, + maxHeight: 500, + }} + scroll="paper" + fullWidth + maxWidth="sm" + > + + Create new user + setOpen(false)} + aria-label="close" + sx={{ + position: "absolute", + right: 8, + top: 8, + }} + > + + + + + + + + + ); +}; + +export default AddUser; diff --git a/typescript/Frontend/src/components/Users/User.tsx b/typescript/Frontend/src/components/Users/User.tsx new file mode 100644 index 000000000..065e6a2b6 --- /dev/null +++ b/typescript/Frontend/src/components/Users/User.tsx @@ -0,0 +1,59 @@ +import { Box, CircularProgress, Alert } from "@mui/material"; +import { AxiosError } from "axios"; +import { useState, useEffect, FC } from "react"; +import { useParams } from "react-router-dom"; +import axiosConfig from "../../config/axiosConfig"; +import { I_User } from "../../util/user.util"; +import UserForm from "./UserForm"; + +interface I_DetailProps { + hasMoveButton?: boolean; +} +const Detail = (props: I_DetailProps) => { + const { id } = useParams(); + const [values, setValues] = useState(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(); + + useEffect(() => { + setLoading(true); + axiosConfig + .get("/GetUserById?id=" + id) + .then((res) => { + setValues(res.data); + setLoading(false); + }) + .catch((err: AxiosError) => { + setError(err); + setLoading(false); + }); + }, [id]); + + const handleUpdate = (formikValues: Partial) => { + return axiosConfig.put("/UpdateUser", { + ...formikValues, + id: id, + }); + }; + + if (values && values.id && values.id.toString() === id) { + return ; + } else if (loading) { + return ( + + + + ); + } else if (error) { + return ( + + {error.message} + + ); + } + return null; +}; + +export default Detail; diff --git a/typescript/Frontend/src/components/Users/UserForm.tsx b/typescript/Frontend/src/components/Users/UserForm.tsx index 62255d090..c0b9474b9 100644 --- a/typescript/Frontend/src/components/Users/UserForm.tsx +++ b/typescript/Frontend/src/components/Users/UserForm.tsx @@ -1,35 +1,31 @@ -import { Alert, Button, Grid, Snackbar } from "@mui/material"; +import { Alert, Grid, Snackbar } from "@mui/material"; +import { AxiosResponse } from "axios"; import { useFormik } from "formik"; import { useState } from "react"; import { FormattedMessage, useIntl } from "react-intl"; -import axiosConfig from "../../config/axiosConfig"; import { I_User } from "../../util/user.util"; +import InnovenergyButton from "../Layout/InnovenergyButton"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; interface I_UserFormProps { - values: I_User; - id: string; + handleSubmit: (formikValues: Partial) => Promise; + values?: I_User; } const UserForm = (props: I_UserFormProps) => { - const { values, id } = props; + const { values, handleSubmit } = props; const [open, setOpen] = useState(false); const intl = useIntl(); const formik = useFormik({ initialValues: { - email: values.email, - information: values.information, + email: values ? values.email : "", + information: values ? values.information : "", }, onSubmit: (formikValues) => { - axiosConfig - .put("/UpdateUser", { - ...formikValues, - id: id, - }) - .then(() => { - setOpen(true); - }); + handleSubmit(formikValues).then(() => { + setOpen(true); + }); }, }); @@ -60,9 +56,9 @@ const UserForm = (props: I_UserFormProps) => { handleChange={formik.handleChange} /> - + { if (path) { @@ -17,26 +17,26 @@ const getPathWithoutId = (path?: string) => { return routes.user; }; -const filterData = ( - searchQuery: string, - data: I_Installation[] | undefined -) => { +const filterData = (searchQuery: string, data: I_User[] | undefined) => { if (data) { - return data.filter( - (installation) => - installation.name.toLowerCase().includes(searchQuery.toLowerCase()) || - installation.location.toLowerCase().includes(searchQuery.toLowerCase()) + return data.filter((installation) => + installation.name.toLowerCase().includes(searchQuery.toLowerCase()) ); } return data; }; -const UserList = () => { - const { availableUsers } = useContext(UserContext); +interface UserListProps { + searchQuery: string; +} + +const UserList = (props: UserListProps) => { + const { availableUsers } = useContext(UsersContext); + const filteredData = filterData(props.searchQuery, availableUsers); const routeMatch = useRouteMatch([routes.users + routes.user + ":id"]); - if (availableUsers && availableUsers.length) { + if (filteredData && filteredData.length) { return ( { component="nav" aria-labelledby="nested-list-subheader" > - {availableUsers.map((user) => { + {filteredData.map((user) => { return ( { const routeMatch = useRouteMatch([routes.users + routes.user + ":id"]); @@ -17,11 +16,11 @@ const UserTabs = () => { return ( - - { component={Link} to={routes.user + id} /> - +
); diff --git a/typescript/Frontend/src/components/Users/Users.tsx b/typescript/Frontend/src/components/Users/Users.tsx index 0499b1974..6b3b9e663 100644 --- a/typescript/Frontend/src/components/Users/Users.tsx +++ b/typescript/Frontend/src/components/Users/Users.tsx @@ -1,38 +1,29 @@ import { Grid } from "@mui/material"; -import { Container } from "@mui/system"; import { Routes, Route } from "react-router"; import routes from "../../routes.json"; -import { I_User } from "../../util/user.util"; -import UserContextProvider from "../Context/UserContextProvider"; -import Detail from "../Layout/Detail"; -import UserForm from "./UserForm"; +import UsersContextProvider from "../Context/UsersContextProvider"; +import SearchSidebar from "../Layout/Search"; +import AddUser from "./AddUser"; +import User from "./User"; import UserList from "./UserList"; import UserTabs from "./UserTabs"; const Users = () => { return ( - - + + - + + - - route="/GetUserById?id=" - formComponent={UserForm} - /> - } - index - /> + } /> - + ); }; diff --git a/typescript/Frontend/src/lang/en.json b/typescript/Frontend/src/lang/en.json index 45abb09c2..35a821433 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" } diff --git a/typescript/Frontend/src/util/installation.util.tsx b/typescript/Frontend/src/util/installation.util.tsx index e63e20506..e1719c224 100644 --- a/typescript/Frontend/src/util/installation.util.tsx +++ b/typescript/Frontend/src/util/installation.util.tsx @@ -7,7 +7,6 @@ export const StyledTab = styled((props: any) => ( fontWeight: theme.typography.fontWeightRegular, fontSize: theme.typography.pxToRem(15), marginRight: theme.spacing(1), - color: "#0d6efd", background: "0 0", border: "1px solid transparent", borderTopLeftRadius: "0.25rem", @@ -19,7 +18,7 @@ export const StyledTab = styled((props: any) => ( color: "#495057", backgroundColor: "#fff", borderColor: "#dee2e6 #dee2e6 #fff", - marginBottom: "-2px", + marginBottom: "-3px", }, "&.Mui-focusVisible": { backgroundColor: "rgba(100, 95, 228, 0.32)", @@ -28,7 +27,6 @@ export const StyledTab = styled((props: any) => ( export const AntTabs = styled(Tabs)({ borderBottom: "1px solid #dee2e6", - overflow: "visible!important", "& div.MuiTabs-scroller": { overflow: "visible!important", diff --git a/typescript/Frontend/src/util/types.tsx b/typescript/Frontend/src/util/types.tsx index d77743e4a..777cc8c80 100644 --- a/typescript/Frontend/src/util/types.tsx +++ b/typescript/Frontend/src/util/types.tsx @@ -1,10 +1,10 @@ // TODO add if required or not export interface I_Installation { type: string; - title: string; - status: number; - detail: string; - instance: string; + title?: string; + status?: number; + detail?: string; + instance?: string; location: string; region: string; country: string; @@ -16,6 +16,7 @@ export interface I_Installation { name: string; information: string; parentId: number; + s3Url: string; } export interface I_Folder {