Fix a few small bugs, revert Detail Component

This commit is contained in:
Sina Blattmann 2023-04-03 15:55:50 +02:00
parent 156eee6c9c
commit 30c9752f9a
35 changed files with 447 additions and 215 deletions

View File

@ -7,7 +7,6 @@ import { IntlProvider } from "react-intl";
import { useState } from "react"; import { useState } from "react";
import en from "./lang/en.json"; import en from "./lang/en.json";
import de from "./lang/de.json"; import de from "./lang/de.json";
import Installations from "./components/Installations/Installations";
import LanguageSelect from "./components/Layout/LanguageSelect"; import LanguageSelect from "./components/Layout/LanguageSelect";
import LogoutButton from "./components/Layout/LogoutButton"; import LogoutButton from "./components/Layout/LogoutButton";
import Users from "./components/Users/Users"; import Users from "./components/Users/Users";
@ -36,7 +35,7 @@ const App = () => {
locale={language} locale={language}
defaultLocale="EN" defaultLocale="EN"
> >
<Container maxWidth="xl" sx={{ margin: 2 }}> <Container maxWidth="xl" sx={{ marginTop: 2 }}>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={3}> <Grid item xs={3}>
<NavigationButtons /> <NavigationButtons />

View File

@ -1,8 +1,9 @@
import React, { useState } from "react"; 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 Container from "@mui/material/Container";
import { axiosConfigWithoutToken } from "./config/axiosConfig"; import { axiosConfigWithoutToken } from "./config/axiosConfig";
import InnovenergyTextfield from "./components/Layout/InnovenergyTextfield"; import InnovenergyTextfield from "./components/Layout/InnovenergyTextfield";
import InnovenergyButton from "./components/Layout/InnovenergyButton";
const loginUser = async (username: string, password: string) => { const loginUser = async (username: string, password: string) => {
return axiosConfigWithoutToken.post("/Login", null, { return axiosConfigWithoutToken.post("/Login", null, {
@ -67,9 +68,9 @@ const Login = ({ setToken, setLanguage }: I_LoginProps) => {
/> />
{error && <Alert severity="error">Incorrect username or password</Alert>} {error && <Alert severity="error">Incorrect username or password</Alert>}
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}> <Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
<Button variant="outlined" onClick={handleSubmit} sx={{ my: 1 }}> <InnovenergyButton onClick={handleSubmit} sx={{ my: 1 }}>
Login Login
</Button> </InnovenergyButton>
</Grid> </Grid>
{loading && <CircularProgress />} {loading && <CircularProgress />}
</Container> </Container>

View File

@ -11,26 +11,26 @@ import axiosConfig from "../../config/axiosConfig";
import { I_User, UserWithInheritedAccess } from "../../util/user.util"; import { I_User, UserWithInheritedAccess } from "../../util/user.util";
import { GroupContext } from "./GroupContextProvider"; import { GroupContext } from "./GroupContextProvider";
interface UserContextProviderProps { interface UsersContextProviderProps {
directAccessUsers: I_User[]; directAccessUsers: I_User[];
setDirectAccessUsers: (value: I_User[]) => void; setDirectAccessUsers: (value: I_User[]) => void;
inheritedAccessUsers: UserWithInheritedAccess[]; inheritedAccessUsers: UserWithInheritedAccess[];
setInheritedAccessUsers: (value: UserWithInheritedAccess[]) => void; setInheritedAccessUsers: (value: UserWithInheritedAccess[]) => void;
availableUsers: I_User[]; availableUsers: I_User[];
setAvailableUsers: (value: I_User[]) => void; setAvailableUsers: (value: I_User[]) => void;
getAvailableUsers: () => I_User[]; getAvailableUsersForResource: () => I_User[];
fetchUsersWithInheritedAccessForResource: () => Promise<void>; fetchUsersWithInheritedAccessForResource: () => Promise<void>;
fetchUsersWithDirectAccessForResource: () => Promise<void>; fetchUsersWithDirectAccessForResource: () => Promise<void>;
} }
export const UserContext = createContext<UserContextProviderProps>({ export const UsersContext = createContext<UsersContextProviderProps>({
directAccessUsers: [], directAccessUsers: [],
setDirectAccessUsers: () => {}, setDirectAccessUsers: () => {},
inheritedAccessUsers: [], inheritedAccessUsers: [],
setInheritedAccessUsers: () => {}, setInheritedAccessUsers: () => {},
availableUsers: [], availableUsers: [],
setAvailableUsers: () => {}, setAvailableUsers: () => {},
getAvailableUsers: () => { getAvailableUsersForResource: () => {
return []; return [];
}, },
fetchUsersWithInheritedAccessForResource: () => { fetchUsersWithInheritedAccessForResource: () => {
@ -41,7 +41,7 @@ export const UserContext = createContext<UserContextProviderProps>({
}, },
}); });
const UserContextProvider = ({ children }: { children: ReactNode }) => { const UsersContextProvider = ({ children }: { children: ReactNode }) => {
const [directAccessUsers, setDirectAccessUsers] = useState<I_User[]>([]); const [directAccessUsers, setDirectAccessUsers] = useState<I_User[]>([]);
const [inheritedAccessUsers, setInheritedAccessUsers] = useState< const [inheritedAccessUsers, setInheritedAccessUsers] = useState<
UserWithInheritedAccess[] UserWithInheritedAccess[]
@ -88,7 +88,7 @@ const UserContextProvider = ({ children }: { children: ReactNode }) => {
}, []); }, []);
return ( return (
<UserContext.Provider <UsersContext.Provider
value={{ value={{
directAccessUsers, directAccessUsers,
setDirectAccessUsers, setDirectAccessUsers,
@ -96,14 +96,14 @@ const UserContextProvider = ({ children }: { children: ReactNode }) => {
setInheritedAccessUsers, setInheritedAccessUsers,
availableUsers, availableUsers,
setAvailableUsers, setAvailableUsers,
getAvailableUsers: getAvailableUsersForResource, getAvailableUsersForResource,
fetchUsersWithInheritedAccessForResource, fetchUsersWithInheritedAccessForResource,
fetchUsersWithDirectAccessForResource, fetchUsersWithDirectAccessForResource,
}} }}
> >
{children} {children}
</UserContext.Provider> </UsersContext.Provider>
); );
}; };
export default UserContextProvider; export default UsersContextProvider;

View File

@ -1,5 +1,5 @@
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
import UserContextProvider from "../../Context/UserContextProvider"; import UsersContextProvider from "../../Context/UsersContextProvider";
import AvailableUserDialog from "./AvailableUserDialog"; import AvailableUserDialog from "./AvailableUserDialog";
import InnovenergyList from "./InnovenergyList"; import InnovenergyList from "./InnovenergyList";
import UsersWithDirectAccess from "./UsersWithDirectAccess"; import UsersWithDirectAccess from "./UsersWithDirectAccess";
@ -7,7 +7,7 @@ import UsersWithInheritedAccess from "./UsersWithInheritedAccess";
const AccessManagement = () => { const AccessManagement = () => {
return ( return (
<UserContextProvider> <UsersContextProvider>
<Grid container sx={{ mt: 1 }}> <Grid container sx={{ mt: 1 }}>
<Grid item xs={6}> <Grid item xs={6}>
<AvailableUserDialog /> <AvailableUserDialog />
@ -17,7 +17,7 @@ const AccessManagement = () => {
</InnovenergyList> </InnovenergyList>
</Grid> </Grid>
</Grid> </Grid>
</UserContextProvider> </UsersContextProvider>
); );
}; };

View File

@ -1,6 +1,5 @@
import { import {
Autocomplete, Autocomplete,
Button,
Dialog, Dialog,
DialogContent, DialogContent,
DialogTitle, DialogTitle,
@ -13,7 +12,8 @@ import { useParams } from "react-router-dom";
import axiosConfig from "../../../config/axiosConfig"; import axiosConfig from "../../../config/axiosConfig";
import { I_User } from "../../../util/user.util"; import { I_User } from "../../../util/user.util";
import { GroupContext } from "../../Context/GroupContextProvider"; import { GroupContext } from "../../Context/GroupContextProvider";
import { UserContext } from "../../Context/UserContextProvider"; import { UsersContext } from "../../Context/UsersContextProvider";
import InnovenergyButton from "../../Layout/InnovenergyButton";
const AvailableUserDialog = () => { const AvailableUserDialog = () => {
const [selectedUsers, setSelectedUsers] = useState<I_User[]>([]); const [selectedUsers, setSelectedUsers] = useState<I_User[]>([]);
@ -22,10 +22,10 @@ const AvailableUserDialog = () => {
const { currentType } = useContext(GroupContext); const { currentType } = useContext(GroupContext);
const { id } = useParams(); const { id } = useParams();
const { const {
getAvailableUsers, getAvailableUsersForResource,
fetchUsersWithDirectAccessForResource, fetchUsersWithDirectAccessForResource,
fetchUsersWithInheritedAccessForResource, fetchUsersWithInheritedAccessForResource,
} = useContext(UserContext); } = useContext(UsersContext);
const handleGrant = () => { const handleGrant = () => {
selectedUsers.forEach((user) => { selectedUsers.forEach((user) => {
@ -63,7 +63,7 @@ const AvailableUserDialog = () => {
sx={{ width: "500px" }} sx={{ width: "500px" }}
multiple multiple
id="tags-standard" id="tags-standard"
options={getAvailableUsers()} options={getAvailableUsersForResource()}
getOptionLabel={(option) => option.name} getOptionLabel={(option) => option.name}
renderInput={(params) => ( renderInput={(params) => (
<TextField <TextField
@ -77,19 +77,18 @@ const AvailableUserDialog = () => {
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button <InnovenergyButton
variant="outlined"
type="submit" type="submit"
sx={{ height: 40, ml: 2 }} sx={{ height: 40, ml: 2 }}
onClick={handleGrant} onClick={handleGrant}
> >
<FormattedMessage id="grantAccess" defaultMessage="Grant access" /> <FormattedMessage id="grantAccess" defaultMessage="Grant access" />
</Button> </InnovenergyButton>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
<Button variant="outlined" type="submit" onClick={() => setOpen(true)}> <InnovenergyButton type="submit" onClick={() => setOpen(true)}>
<FormattedMessage id="grantAccess" defaultMessage="Grant access" /> <FormattedMessage id="grantAccess" defaultMessage="Grant access" />
</Button> </InnovenergyButton>
</> </>
); );
}; };

View File

@ -10,13 +10,13 @@ import { Fragment, useContext, useEffect } from "react";
import axiosConfig from "../../../config/axiosConfig"; import axiosConfig from "../../../config/axiosConfig";
import PersonRemoveIcon from "@mui/icons-material/PersonRemove"; import PersonRemoveIcon from "@mui/icons-material/PersonRemove";
import PersonIcon from "@mui/icons-material/Person"; import PersonIcon from "@mui/icons-material/Person";
import { UserContext } from "../../Context/UserContextProvider"; import { UsersContext } from "../../Context/UsersContextProvider";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { GroupContext } from "../../Context/GroupContextProvider"; import { GroupContext } from "../../Context/GroupContextProvider";
const UsersWithDirectAccess = () => { const UsersWithDirectAccess = () => {
const { fetchUsersWithDirectAccessForResource, directAccessUsers } = const { fetchUsersWithDirectAccessForResource, directAccessUsers } =
useContext(UserContext); useContext(UsersContext);
const { currentType } = useContext(GroupContext); const { currentType } = useContext(GroupContext);
const { id } = useParams(); const { id } = useParams();

View File

@ -13,11 +13,11 @@ import {
} from "../../../util/user.util"; } from "../../../util/user.util";
import routes from "../../../routes.json"; import routes from "../../../routes.json";
import PersonIcon from "@mui/icons-material/Person"; import PersonIcon from "@mui/icons-material/Person";
import { UserContext } from "../../Context/UserContextProvider"; import { UsersContext } from "../../Context/UsersContextProvider";
const UsersWithInheritedAccess = () => { const UsersWithInheritedAccess = () => {
const { fetchUsersWithInheritedAccessForResource, inheritedAccessUsers } = const { fetchUsersWithInheritedAccessForResource, inheritedAccessUsers } =
useContext(UserContext); useContext(UsersContext);
useEffect(() => { useEffect(() => {
fetchUsersWithInheritedAccessForResource(); fetchUsersWithInheritedAccessForResource();

View File

@ -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 { useState } from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import axiosConfig from "../../config/axiosConfig"; import axiosConfig from "../../config/axiosConfig";
import { I_Folder } from "../../util/types"; import { I_Folder } from "../../util/types";
import FolderForm from "./FolderForm"; import FolderForm from "./FolderForm";
import CloseIcon from "@mui/icons-material/Close";
import InnovenergyButton from "../Layout/InnovenergyButton";
interface AddNewDialogProps { interface AddNewDialogProps {
values: I_Folder; values: I_Folder;
@ -13,17 +15,22 @@ const AddNewDialog = (props: AddNewDialogProps) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const handleSubmit = (data: I_Folder, childData: Partial<I_Folder>) => { const handleSubmit = (data: I_Folder, childData: Partial<I_Folder>) => {
return axiosConfig.post("/CreateFolder", { return axiosConfig
...childData, .post("/CreateFolder", {
parentId: data.id, ...childData,
}); parentId: data.id,
})
.then((res) => {
setOpen(false);
return res;
});
}; };
return ( return (
<> <>
<Button variant="outlined" sx={{ ml: 2 }} onClick={() => setOpen(true)}> <InnovenergyButton sx={{ ml: 2 }} onClick={() => setOpen(true)}>
<FormattedMessage id="addNewChild" defaultMessage="Add new child" /> <FormattedMessage id="addNewChild" defaultMessage="Add new child" />
</Button> </InnovenergyButton>
<Dialog <Dialog
onClose={() => setOpen(false)} onClose={() => setOpen(false)}
aria-labelledby="customized-dialog-title" aria-labelledby="customized-dialog-title"
@ -33,8 +40,25 @@ const AddNewDialog = (props: AddNewDialogProps) => {
maxHeight: 500, maxHeight: 500,
}} }}
scroll="paper" scroll="paper"
fullWidth
maxWidth="sm"
> >
<DialogTitle>Create new folder</DialogTitle> <DialogTitle>
Create new folder
<IconButton
edge="start"
color="inherit"
onClick={() => setOpen(false)}
aria-label="close"
sx={{
position: "absolute",
right: 8,
top: 8,
}}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent> <DialogContent>
<FolderForm values={props.values} handleSubmit={handleSubmit} /> <FolderForm values={props.values} handleSubmit={handleSubmit} />
</DialogContent> </DialogContent>

View File

@ -1,4 +1,4 @@
import { Button, CircularProgress, Grid } from "@mui/material"; import { CircularProgress, Grid } from "@mui/material";
import { AxiosResponse } from "axios"; import { AxiosResponse } from "axios";
import { useFormik } from "formik"; import { useFormik } from "formik";
import { ReactNode, useContext, useState } from "react"; import { ReactNode, useContext, useState } from "react";
@ -6,6 +6,7 @@ import { FormattedMessage, useIntl } from "react-intl";
import { I_Folder } from "../../util/types"; import { I_Folder } from "../../util/types";
import { GroupContext } from "../Context/GroupContextProvider"; import { GroupContext } from "../Context/GroupContextProvider";
import InnovenergySnackbar from "../InnovenergySnackbar"; import InnovenergySnackbar from "../InnovenergySnackbar";
import InnovenergyButton from "../Layout/InnovenergyButton";
import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield";
interface I_CustomerFormProps { interface I_CustomerFormProps {
@ -28,8 +29,9 @@ const FolderForm = (props: I_CustomerFormProps) => {
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
name: values.name, name: additionalButtons && additionalButtons.length ? values.name : "",
information: values.information, information:
additionalButtons && additionalButtons.length ? values.information : "",
}, },
onSubmit: (formikValues) => { onSubmit: (formikValues) => {
setLoading(true); setLoading(true);
@ -73,9 +75,9 @@ const FolderForm = (props: I_CustomerFormProps) => {
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}> <Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
{loading && <CircularProgress />} {loading && <CircularProgress />}
{additionalButtons && additionalButtons.map((button) => button)} {additionalButtons && additionalButtons.map((button) => button)}
<Button variant="outlined" type="submit" sx={{ ml: 2 }}> <InnovenergyButton type="submit" sx={{ ml: 2 }}>
<FormattedMessage id="submit" defaultMessage="Submit" /> <FormattedMessage id="submit" defaultMessage="Submit" />
</Button> </InnovenergyButton>
</Grid> </Grid>
<InnovenergySnackbar <InnovenergySnackbar
error={error} error={error}

View File

@ -9,15 +9,17 @@ import { AntTabs, StyledTab } from "../../util/installation.util";
const GroupTabs = () => { const GroupTabs = () => {
const routeMatch = useRouteMatch([ const routeMatch = useRouteMatch([
routes.groups + routes.folder + ":id", routes.installations + routes.tree + routes.folder + ":id",
routes.groups + routes.manageAccess + ":id", routes.installations + routes.tree + routes.manageAccess + ":id",
routes.groups + routes.installation + ":id", routes.installations + routes.tree + routes.installation + ":id",
]); ]);
const id = routeMatch?.params?.id; const id = routeMatch?.params?.id;
const intl = useIntl(); const intl = useIntl();
const { currentType } = useContext(GroupContext); const { currentType } = useContext(GroupContext);
console.log(routeMatch);
if (id) { if (id) {
return ( return (
<Box sx={{ width: "100%" }}> <Box sx={{ width: "100%" }}>
@ -32,7 +34,9 @@ const GroupTabs = () => {
id: "folder", id: "folder",
defaultMessage: "Folder", defaultMessage: "Folder",
})} })}
value={routes.groups + routes.folder + ":id"} value={
routes.installations + routes.tree + routes.folder + ":id"
}
component={Link} component={Link}
to={routes.folder + id} to={routes.folder + id}
/> />
@ -42,7 +46,12 @@ const GroupTabs = () => {
id: "installation", id: "installation",
defaultMessage: "Installation", defaultMessage: "Installation",
})} })}
value={routes.groups + routes.installation + ":id"} value={
routes.installations +
routes.tree +
routes.installation +
":id"
}
component={Link} component={Link}
to={routes.installation + id} to={routes.installation + id}
/> />
@ -53,7 +62,9 @@ const GroupTabs = () => {
id: "manageAccess", id: "manageAccess",
defaultMessage: "Manage access", defaultMessage: "Manage access",
})} })}
value={routes.groups + routes.manageAccess + ":id"} value={
routes.installations + routes.tree + routes.manageAccess + ":id"
}
component={Link} component={Link}
to={routes.manageAccess + id} to={routes.manageAccess + id}
/> />

View File

@ -1,16 +1,12 @@
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
import { Container } from "@mui/system";
import { Routes, Route } from "react-router"; import { Routes, Route } from "react-router";
import routes from "../../routes.json"; import routes from "../../routes.json";
import Folder from "./Folder"; import Folder from "./Folder";
import GroupTabs from "./GroupTabs"; import GroupTabs from "./GroupTabs";
import GroupContextProvider from "../Context/GroupContextProvider"; import GroupContextProvider from "../Context/GroupContextProvider";
import GroupTree from "./Tree/GroupTree"; import GroupTree from "./Tree/GroupTree";
import NavigationButtons from "../Layout/NavigationButtons";
import AccessManagement from "./AccessManagement/AccessManagement"; import AccessManagement from "./AccessManagement/AccessManagement";
import { I_Installation } from "../../util/types"; import Installation from "../Installations/Installation";
import InstallationForm from "../Installations/InstallationForm";
import Detail from "../Layout/Detail";
const Groups = () => { const Groups = () => {
return ( return (
@ -29,12 +25,7 @@ const Groups = () => {
/> />
<Route <Route
path={routes.installation + ":id"} path={routes.installation + ":id"}
element={ element={<Installation hasMoveButton />}
<Detail<I_Installation>
route="/GetInstallationById?id="
formComponent={InstallationForm}
/>
}
/> />
</Routes> </Routes>
</Grid> </Grid>

View File

@ -1,10 +1,10 @@
import { Button, Grid, InputLabel } from "@mui/material"; import { Grid, InputLabel } from "@mui/material";
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import axiosConfig from "../../config/axiosConfig"; import axiosConfig from "../../config/axiosConfig";
import { GroupContext } from "../Context/GroupContextProvider"; import { GroupContext } from "../Context/GroupContextProvider";
import MoveTree from "./Tree/MoveTree"; import InnovenergyButton from "../Layout/InnovenergyButton";
interface LocationFormProps { interface LocationFormProps {
parentId: number; parentId: number;
@ -39,13 +39,9 @@ const LocationForm = (props: LocationFormProps) => {
</Grid> </Grid>
<Grid item xs={9} display="inline"></Grid> <Grid item xs={9} display="inline"></Grid>
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}> <Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
<Button <InnovenergyButton sx={{ height: 40, ml: 2 }} onClick={handleMove}>
variant="outlined"
sx={{ height: 40, ml: 2 }}
onClick={handleMove}
>
<FormattedMessage id="move" defaultMessage="Move" /> <FormattedMessage id="move" defaultMessage="Move" />
</Button> </InnovenergyButton>
</Grid> </Grid>
</Grid> </Grid>
); );

View File

@ -19,14 +19,6 @@ const GroupTree = () => {
fetchData(); fetchData();
}, [fetchData]); }, [fetchData]);
/* useEffect(() => {
if (id) {
setSelected(
currentType === "Folder" ? "Folder" + id : "Installation" + id
);
}
}, [id, currentType]); */
const getNodes = (element: I_Folder | I_Installation): null | ReactNode => { const getNodes = (element: I_Folder | I_Installation): null | ReactNode => {
if (instanceOfFolder(element)) { if (instanceOfFolder(element)) {
return element.children ? renderTree(element.children) : null; return element.children ? renderTree(element.children) : null;
@ -55,6 +47,9 @@ const GroupTree = () => {
nodeId={element.type + element.id} nodeId={element.type + element.id}
label={element.name} label={element.name}
onClick={() => setCurrentType(element.type)} onClick={() => setCurrentType(element.type)}
sx={{
".MuiTreeItem-content": { paddingY: "12px" },
}}
> >
{getNodes(element)} {getNodes(element)}
</TreeItem> </TreeItem>
@ -75,7 +70,12 @@ const GroupTree = () => {
aria-label="rich object" aria-label="rich object"
defaultCollapseIcon={<ExpandMoreIcon />} defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />} defaultExpandIcon={<ChevronRightIcon />}
sx={{ height: 300, flexGrow: 1, maxWidth: 400 }} sx={{
height: 480,
flexGrow: 1,
overflow: "auto",
".MuiTreeView-root": { overflowX: "hidden" },
}}
expanded={openNodes} expanded={openNodes}
onNodeToggle={(e, ids) => { onNodeToggle={(e, ids) => {
setOpenNodes(ids); setOpenNodes(ids);

View File

@ -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 DialogContent from "@mui/material/DialogContent";
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
@ -6,6 +6,7 @@ import { useParams } from "react-router-dom";
import axiosConfig from "../../../config/axiosConfig"; import axiosConfig from "../../../config/axiosConfig";
import { I_Folder } from "../../../util/types"; import { I_Folder } from "../../../util/types";
import { GroupContext } from "../../Context/GroupContextProvider"; import { GroupContext } from "../../Context/GroupContextProvider";
import InnovenergyButton from "../../Layout/InnovenergyButton";
import MoveTree from "./MoveTree"; import MoveTree from "./MoveTree";
interface MoveDialogProps { interface MoveDialogProps {
@ -37,9 +38,9 @@ const MoveDialog = (props: MoveDialogProps) => {
return ( return (
<div> <div>
<Button variant="outlined" onClick={() => setOpen(true)}> <InnovenergyButton onClick={() => setOpen(true)} sx={{ mr: 1 }}>
<FormattedMessage id="move" defaultMessage="Move" /> <FormattedMessage id="move" defaultMessage="Move" />
</Button> </InnovenergyButton>
<Dialog <Dialog
onClose={() => setOpen(false)} onClose={() => setOpen(false)}
aria-labelledby="customized-dialog-title" aria-labelledby="customized-dialog-title"
@ -58,9 +59,7 @@ const MoveDialog = (props: MoveDialogProps) => {
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button autoFocus onClick={handleMove}> <InnovenergyButton onClick={handleMove}>Move</InnovenergyButton>
Move
</Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
</div> </div>

View File

@ -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<I_Installation>();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<AxiosError>();
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 (
<Box sx={{ py: 3 }}>
<InstallationForm
values={values}
id={id}
hasMoveButton={props.hasMoveButton}
/>
</Box>
);
} else if (loading) {
return (
<Box
sx={{ width: 1 / 2, justifyContent: "center", display: "flex", mt: 10 }}
>
<CircularProgress sx={{ m: 2 }} />
</Box>
);
} else if (error) {
return (
<Alert severity="error" sx={{ mt: 1 }}>
{error.message}
</Alert>
);
}
return null;
};
export default Installation;

View File

@ -1,21 +1,25 @@
import { Alert, Button, Grid, Snackbar } from "@mui/material"; import { Alert, Grid, Snackbar } from "@mui/material";
import { useFormik } from "formik"; import { useFormik } from "formik";
import { useState } from "react"; import { useContext, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import axiosConfig from "../../config/axiosConfig"; import axiosConfig from "../../config/axiosConfig";
import { I_Installation } from "../../util/types"; import { I_Installation } from "../../util/types";
import { InstallationContext } from "../Context/InstallationContextProvider";
import MoveDialog from "../Groups/Tree/MoveDialog"; import MoveDialog from "../Groups/Tree/MoveDialog";
import InnovenergyButton from "../Layout/InnovenergyButton";
import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield";
interface I_InstallationFormProps { interface I_InstallationFormProps {
values: I_Installation; values: I_Installation;
id: string | undefined; id: string | undefined;
hasMoveButton?: boolean;
} }
const InstallationForm = (props: I_InstallationFormProps) => { const InstallationForm = (props: I_InstallationFormProps) => {
const { values, id } = props; const { values, id, hasMoveButton } = props;
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const intl = useIntl(); const intl = useIntl();
const { fetchData } = useContext(InstallationContext);
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
@ -33,6 +37,7 @@ const InstallationForm = (props: I_InstallationFormProps) => {
}) })
.then(() => { .then(() => {
setOpen(true); setOpen(true);
fetchData();
}); });
}, },
}); });
@ -94,10 +99,10 @@ const InstallationForm = (props: I_InstallationFormProps) => {
handleChange={formik.handleChange} handleChange={formik.handleChange}
/> />
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}> <Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
<MoveDialog values={values} /> {hasMoveButton && <MoveDialog values={values} />}
<Button variant="outlined" type="submit"> <InnovenergyButton type="submit">
<FormattedMessage id="applyChanges" defaultMessage="Apply changes" /> <FormattedMessage id="applyChanges" defaultMessage="Apply changes" />
</Button> </InnovenergyButton>
</Grid> </Grid>
<Snackbar <Snackbar
open={open} open={open}

View File

@ -41,9 +41,9 @@ const InstallationList = (props: InstallationListProps) => {
const filteredData = filterData(props.searchQuery, data); const filteredData = filterData(props.searchQuery, data);
const routeMatch = useRouteMatch([ const routeMatch = useRouteMatch([
routes.installations + routes.installation + ":id", routes.installations + routes.list + routes.installation + ":id",
routes.installations + routes.alarms + ":id", routes.installations + routes.list + routes.alarms + ":id",
routes.installations + routes.log + ":id", routes.installations + routes.list + routes.log + ":id",
]); ]);
useEffect(() => { useEffect(() => {
@ -95,7 +95,6 @@ const InstallationList = (props: InstallationListProps) => {
</List> </List>
); );
} else if (error) { } else if (error) {
console.log(error);
return ( return (
<Alert severity="error" sx={{ mt: 1 }}> <Alert severity="error" sx={{ mt: 1 }}>
{error.message} {error.message}

View File

@ -1,12 +1,9 @@
import * as React from "react"; import * as React from "react";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import Box from "@mui/material/Box"; 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 routes from "../../routes.json";
import useRouteMatch from "../../hooks/useRouteMatch"; import useRouteMatch from "../../hooks/useRouteMatch";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { styled } from "@mui/material";
import { AntTabs, StyledTab } from "../../util/installation.util"; import { AntTabs, StyledTab } from "../../util/installation.util";
const InstallationTabs = () => { const InstallationTabs = () => {
@ -25,7 +22,7 @@ const InstallationTabs = () => {
<Box sx={{ borderBottom: 1, borderColor: "divider" }}> <Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<AntTabs <AntTabs
value={routeMatch?.pattern?.path ?? routes.installation + ":id"} value={routeMatch?.pattern?.path ?? routes.installation + ":id"}
aria-label="basic tabs example" aria-label="installation tabs"
> >
<StyledTab <StyledTab
label={intl.formatMessage({ label={intl.formatMessage({

View File

@ -1,35 +1,27 @@
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
import { Container } from "@mui/system";
import { Routes, Route } from "react-router"; import { Routes, Route } from "react-router";
import NavigationButtons from "../Layout/NavigationButtons";
import Sidebar from "../Layout/Sidebar";
import Alarms from "./Alarms"; import Alarms from "./Alarms";
import InstallationTabs from "./InstallationTabs"; import InstallationTabs from "./InstallationTabs";
import Log from "./Log"; import Log from "./Log";
import routes from "../../routes.json"; import routes from "../../routes.json";
import InstallationContextProvider from "../Context/InstallationContextProvider"; import InstallationContextProvider from "../Context/InstallationContextProvider";
import Detail from "../Layout/Detail"; import SearchSidebar from "../Layout/Search";
import { I_Installation } from "../../util/types"; import InstallationList from "./InstallationList";
import InstallationForm from "./InstallationForm"; import Installation from "./Installation";
const Installations = () => { const Installations = () => {
return ( return (
<InstallationContextProvider> <InstallationContextProvider>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={3}> <Grid item xs={3}>
<Sidebar /> <SearchSidebar listComponent={InstallationList} />
</Grid> </Grid>
<Grid item xs={9}> <Grid item xs={9}>
<InstallationTabs /> <InstallationTabs />
<Routes> <Routes>
<Route <Route
path={routes.installation + ":id"} path={routes.installation + ":id"}
element={ element={<Installation />}
<Detail<I_Installation>
route="/GetInstallationById?id="
formComponent={InstallationForm}
/>
}
index index
/> />
<Route path={routes.alarms + ":id"} element={<Alarms />} /> <Route path={routes.alarms + ":id"} element={<Alarms />} />

View File

@ -149,7 +149,6 @@ const Log = () => {
return previous; return previous;
}, {}); }, {});
console.log(flattenObject(foo.Devices[0]));
return <div>log</div>; return <div>log</div>;
}; };

View File

@ -6,14 +6,16 @@ import axiosConfig from "../../config/axiosConfig";
export interface I_FormProps<T> { export interface I_FormProps<T> {
values: T; values: T;
id: string; id: string;
hasMoveButton?: boolean;
} }
interface I_DetailProps<T> { interface I_DetailProps<T> {
formComponent: FC<I_FormProps<T>>; formComponent: FC<I_FormProps<T>>;
route: string; route: string;
hasMoveButton?: boolean;
} }
const Detail = <T extends { id: number }>(props: I_DetailProps<T>) => { const Detail = <T extends { id: number }>(props: I_DetailProps<T>) => {
const { id } = useParams(); const { id } = useParams();
const { formComponent: FormComponent, route } = props; const { formComponent: FormComponent, route, hasMoveButton } = props;
const [values, setValues] = useState<T>(); const [values, setValues] = useState<T>();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<AxiosError>(); const [error, setError] = useState<AxiosError>();
@ -33,7 +35,9 @@ const Detail = <T extends { id: number }>(props: I_DetailProps<T>) => {
}, [id, route]); }, [id, route]);
if (values && values.id && values.id.toString() === id) { if (values && values.id && values.id.toString() === id) {
return <FormComponent values={values} id={id} />; return (
<FormComponent values={values} id={id} hasMoveButton={hasMoveButton} />
);
} else if (loading) { } else if (loading) {
return ( return (
<Box <Box

View File

@ -0,0 +1,24 @@
import { Button, SxProps, Theme } from "@mui/material";
import { ReactNode } from "react";
interface I_InnovenergyButtonProps {
children?: ReactNode;
type?: "button" | "submit" | "reset" | undefined;
onClick?: React.MouseEventHandler<HTMLButtonElement>;
sx?: SxProps<Theme> | undefined;
}
const InnovenergyButton = (props: I_InnovenergyButtonProps) => {
return (
<Button
variant="contained"
type={props.type}
onClick={props.onClick}
sx={props.sx}
>
{props.children}
</Button>
);
};
export default InnovenergyButton;

View File

@ -1,7 +1,7 @@
import { Button } from "@mui/material";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import axiosConfig from "../../config/axiosConfig"; import axiosConfig from "../../config/axiosConfig";
import InnovenergyButton from "./InnovenergyButton";
interface LogoutButtonProps { interface LogoutButtonProps {
removeToken: () => void; removeToken: () => void;
@ -10,8 +10,7 @@ const LogoutButton = (props: LogoutButtonProps) => {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<Button <InnovenergyButton
variant="outlined"
onClick={() => { onClick={() => {
axiosConfig.post("/Logout").then(() => { axiosConfig.post("/Logout").then(() => {
navigate("/"); navigate("/");
@ -21,7 +20,7 @@ const LogoutButton = (props: LogoutButtonProps) => {
sx={{ mx: 1 }} sx={{ mx: 1 }}
> >
<FormattedMessage id="logout" defaultMessage="Logout" /> <FormattedMessage id="logout" defaultMessage="Logout" />
</Button> </InnovenergyButton>
); );
}; };

View File

@ -3,7 +3,7 @@ import useRouteMatch from "../../hooks/useRouteMatch";
import routes from "../../routes.json"; import routes from "../../routes.json";
import ListIcon from "@mui/icons-material/List"; import ListIcon from "@mui/icons-material/List";
import AccountTreeIcon from "@mui/icons-material/AccountTree"; import AccountTreeIcon from "@mui/icons-material/AccountTree";
import { AntTabs, StyledTab } from "../../util/installation.util"; import { ToggleButtonGroup, ToggleButton } from "@mui/material";
const ModeButtons = () => { const ModeButtons = () => {
const routeMatch = useRouteMatch([ const routeMatch = useRouteMatch([
@ -13,7 +13,7 @@ const ModeButtons = () => {
return ( return (
<> <>
<AntTabs {/* <AntTabs
value={routeMatch?.pattern?.path ?? routes.installation + ":id"} value={routeMatch?.pattern?.path ?? routes.installation + ":id"}
aria-label="basic tabs example" aria-label="basic tabs example"
> >
@ -29,8 +29,8 @@ const ModeButtons = () => {
component={Link} component={Link}
to={routes.tree} to={routes.tree}
/> />
</AntTabs> </AntTabs> */}
{/* <ToggleButtonGroup <ToggleButtonGroup
color="primary" color="primary"
value={routeMatch?.pattern?.path} value={routeMatch?.pattern?.path}
exclusive exclusive
@ -51,7 +51,7 @@ const ModeButtons = () => {
> >
<AccountTreeIcon /> <AccountTreeIcon />
</ToggleButton> </ToggleButton>
</ToggleButtonGroup> */} </ToggleButtonGroup>
</> </>
); );
}; };

View File

@ -1,8 +1,8 @@
import { ToggleButton, ToggleButtonGroup } from "@mui/material";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import useRouteMatch from "../../hooks/useRouteMatch"; import useRouteMatch from "../../hooks/useRouteMatch";
import routes from "../../routes.json"; import routes from "../../routes.json";
import { AntTabs, StyledTab } from "../../util/installation.util";
const NavigationButtons = () => { const NavigationButtons = () => {
const routeMatch = useRouteMatch([ const routeMatch = useRouteMatch([
@ -11,28 +11,52 @@ const NavigationButtons = () => {
]); ]);
return ( return (
<ToggleButtonGroup <>
color="primary" {/* <ToggleButtonGroup
value={routeMatch?.pattern?.path} color="primary"
exclusive value={routeMatch?.pattern?.path}
sx={{ mb: 1 }} exclusive
fullWidth sx={{ mb: 1 }}
> fullWidth
<ToggleButton
value={routes.installations + "*"}
component={Link}
to={routes.installations}
> >
<FormattedMessage id="installations" defaultMessage="Installations" /> <ToggleButton
</ToggleButton> value={routes.installations + "*"}
<ToggleButton component={Link}
value={routes.users + "*"} to={routes.installations}
component={Link} >
to={routes.users} <FormattedMessage id="installations" defaultMessage="Installations" />
</ToggleButton>
<ToggleButton
value={routes.users + "*"}
component={Link}
to={routes.users}
>
<FormattedMessage id="users" defaultMessage="Users" />
</ToggleButton>
</ToggleButtonGroup> */}
<AntTabs
value={routeMatch?.pattern?.path}
aria-label="basic tabs example"
> >
<FormattedMessage id="users" defaultMessage="Users" /> <StyledTab
</ToggleButton> value={routes.installations + "*"}
</ToggleButtonGroup> component={Link}
to={routes.installations + routes.list}
label={
<FormattedMessage
id="installations"
defaultMessage="Installations"
/>
}
/>
<StyledTab
value={routes.users + "*"}
component={Link}
to={routes.users}
label={<FormattedMessage id="users" defaultMessage="Users" />}
/>
</AntTabs>
</>
); );
}; };

View File

@ -1,9 +1,12 @@
import { TextField } from "@mui/material"; import { TextField } from "@mui/material";
import { useState } from "react"; import { FC, useState } from "react";
import { useIntl } from "react-intl"; 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 [searchQuery, setSearchQuery] = useState("");
const intl = useIntl(); const intl = useIntl();
@ -20,10 +23,9 @@ const Sidebar = () => {
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
/> />
<ListComponent searchQuery={searchQuery} />
<InstallationList searchQuery={searchQuery} />
</> </>
); );
}; };
export default Sidebar; export default SearchSidebar;

View File

@ -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<I_User>) => {
return axiosConfig.post("/CreateUser", data).then((res) => {
setOpen(false);
return res;
});
};
return (
<>
<InnovenergyButton sx={{ my: 1 }} onClick={() => setOpen(true)}>
<FormattedMessage id="addNewChild" defaultMessage="Add user" />
</InnovenergyButton>
<Dialog
onClose={() => setOpen(false)}
aria-labelledby="customized-dialog-title"
open={open}
sx={{
".MuiDialogContent-root": { overflowX: "hidden" },
maxHeight: 500,
}}
scroll="paper"
fullWidth
maxWidth="sm"
>
<DialogTitle>
Create new user
<IconButton
edge="start"
color="inherit"
onClick={() => setOpen(false)}
aria-label="close"
sx={{
position: "absolute",
right: 8,
top: 8,
}}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent>
<UserForm handleSubmit={handleSubmit} />
</DialogContent>
</Dialog>
</>
);
};
export default AddUser;

View File

@ -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<T> {
hasMoveButton?: boolean;
}
const Detail = <T extends { id: number }>(props: I_DetailProps<T>) => {
const { id } = useParams();
const [values, setValues] = useState<I_User>();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<AxiosError>();
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<I_User>) => {
return axiosConfig.put("/UpdateUser", {
...formikValues,
id: id,
});
};
if (values && values.id && values.id.toString() === id) {
return <UserForm values={values} handleSubmit={handleUpdate} />;
} else if (loading) {
return (
<Box
sx={{ width: 1 / 2, justifyContent: "center", display: "flex", mt: 10 }}
>
<CircularProgress sx={{ m: 2 }} />
</Box>
);
} else if (error) {
return (
<Alert severity="error" sx={{ mt: 1 }}>
{error.message}
</Alert>
);
}
return null;
};
export default Detail;

View File

@ -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 { useFormik } from "formik";
import { useState } from "react"; import { useState } from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import axiosConfig from "../../config/axiosConfig";
import { I_User } from "../../util/user.util"; import { I_User } from "../../util/user.util";
import InnovenergyButton from "../Layout/InnovenergyButton";
import InnovenergyTextfield from "../Layout/InnovenergyTextfield"; import InnovenergyTextfield from "../Layout/InnovenergyTextfield";
interface I_UserFormProps { interface I_UserFormProps {
values: I_User; handleSubmit: (formikValues: Partial<I_User>) => Promise<AxiosResponse>;
id: string; values?: I_User;
} }
const UserForm = (props: I_UserFormProps) => { const UserForm = (props: I_UserFormProps) => {
const { values, id } = props; const { values, handleSubmit } = props;
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const intl = useIntl(); const intl = useIntl();
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
email: values.email, email: values ? values.email : "",
information: values.information, information: values ? values.information : "",
}, },
onSubmit: (formikValues) => { onSubmit: (formikValues) => {
axiosConfig handleSubmit(formikValues).then(() => {
.put("/UpdateUser", { setOpen(true);
...formikValues, });
id: id,
})
.then(() => {
setOpen(true);
});
}, },
}); });
@ -60,9 +56,9 @@ const UserForm = (props: I_UserFormProps) => {
handleChange={formik.handleChange} handleChange={formik.handleChange}
/> />
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}> <Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
<Button variant="outlined" type="submit"> <InnovenergyButton type="submit">
<FormattedMessage id="applyChanges" defaultMessage="Apply changes" /> <FormattedMessage id="applyChanges" defaultMessage="Apply changes" />
</Button> </InnovenergyButton>
</Grid> </Grid>
<Snackbar <Snackbar
open={open} open={open}

View File

@ -6,8 +6,8 @@ import { Link } from "react-router-dom";
import useRouteMatch from "../../hooks/useRouteMatch"; import useRouteMatch from "../../hooks/useRouteMatch";
import routes from "../../routes.json"; import routes from "../../routes.json";
import { Fragment, useContext } from "react"; import { Fragment, useContext } from "react";
import { I_Installation } from "../../util/types"; import { UsersContext } from "../Context/UsersContextProvider";
import { UserContext } from "../Context/UserContextProvider"; import { I_User } from "../../util/user.util";
const getPathWithoutId = (path?: string) => { const getPathWithoutId = (path?: string) => {
if (path) { if (path) {
@ -17,26 +17,26 @@ const getPathWithoutId = (path?: string) => {
return routes.user; return routes.user;
}; };
const filterData = ( const filterData = (searchQuery: string, data: I_User[] | undefined) => {
searchQuery: string,
data: I_Installation[] | undefined
) => {
if (data) { if (data) {
return data.filter( return data.filter((installation) =>
(installation) => installation.name.toLowerCase().includes(searchQuery.toLowerCase())
installation.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
installation.location.toLowerCase().includes(searchQuery.toLowerCase())
); );
} }
return data; return data;
}; };
const UserList = () => { interface UserListProps {
const { availableUsers } = useContext(UserContext); searchQuery: string;
}
const UserList = (props: UserListProps) => {
const { availableUsers } = useContext(UsersContext);
const filteredData = filterData(props.searchQuery, availableUsers);
const routeMatch = useRouteMatch([routes.users + routes.user + ":id"]); const routeMatch = useRouteMatch([routes.users + routes.user + ":id"]);
if (availableUsers && availableUsers.length) { if (filteredData && filteredData.length) {
return ( return (
<List <List
sx={{ sx={{
@ -51,7 +51,7 @@ const UserList = () => {
component="nav" component="nav"
aria-labelledby="nested-list-subheader" aria-labelledby="nested-list-subheader"
> >
{availableUsers.map((user) => { {filteredData.map((user) => {
return ( return (
<Fragment key={user.id}> <Fragment key={user.id}>
<Link <Link

View File

@ -1,11 +1,10 @@
import * as React from "react"; import * as React from "react";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import routes from "../../routes.json"; import routes from "../../routes.json";
import useRouteMatch from "../../hooks/useRouteMatch"; import useRouteMatch from "../../hooks/useRouteMatch";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { AntTabs, StyledTab } from "../../util/installation.util";
const UserTabs = () => { const UserTabs = () => {
const routeMatch = useRouteMatch([routes.users + routes.user + ":id"]); const routeMatch = useRouteMatch([routes.users + routes.user + ":id"]);
@ -17,11 +16,11 @@ const UserTabs = () => {
return ( return (
<Box sx={{ width: "100%" }}> <Box sx={{ width: "100%" }}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}> <Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs <AntTabs
value={routeMatch?.pattern?.path ?? routes.user + ":id"} value={routeMatch?.pattern?.path ?? routes.user + ":id"}
aria-label="basic tabs example" aria-label="user tabs"
> >
<Tab <StyledTab
label={intl.formatMessage({ label={intl.formatMessage({
id: "user", id: "user",
defaultMessage: "User", defaultMessage: "User",
@ -30,7 +29,7 @@ const UserTabs = () => {
component={Link} component={Link}
to={routes.user + id} to={routes.user + id}
/> />
</Tabs> </AntTabs>
</Box> </Box>
</Box> </Box>
); );

View File

@ -1,38 +1,29 @@
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
import { Container } from "@mui/system";
import { Routes, Route } from "react-router"; import { Routes, Route } from "react-router";
import routes from "../../routes.json"; import routes from "../../routes.json";
import { I_User } from "../../util/user.util"; import UsersContextProvider from "../Context/UsersContextProvider";
import UserContextProvider from "../Context/UserContextProvider"; import SearchSidebar from "../Layout/Search";
import Detail from "../Layout/Detail"; import AddUser from "./AddUser";
import UserForm from "./UserForm"; import User from "./User";
import UserList from "./UserList"; import UserList from "./UserList";
import UserTabs from "./UserTabs"; import UserTabs from "./UserTabs";
const Users = () => { const Users = () => {
return ( return (
<UserContextProvider> <UsersContextProvider>
<Grid container spacing={2}> <Grid container>
<Grid item xs={3}> <Grid item xs={3}>
<UserList /> <AddUser />
<SearchSidebar listComponent={UserList} />
</Grid> </Grid>
<Grid item xs={9}> <Grid item xs={9}>
<UserTabs /> <UserTabs />
<Routes> <Routes>
<Route <Route path={routes.user + ":id"} element={<User />} />
path={routes.user + ":id"}
element={
<Detail<I_User>
route="/GetUserById?id="
formComponent={UserForm}
/>
}
index
/>
</Routes> </Routes>
</Grid> </Grid>
</Grid> </Grid>
</UserContextProvider> </UsersContextProvider>
); );
}; };

View File

@ -17,6 +17,6 @@
"updatedSuccessfully": "Updated successfully", "updatedSuccessfully": "Updated successfully",
"groups": "Groups", "groups": "Groups",
"group": "Group", "group": "Group",
"folder": "folder", "folder": "Folder",
"updateFolderErrorMessage": "Couldn't update folder, an error occured" "updateFolderErrorMessage": "Couldn't update folder, an error occured"
} }

View File

@ -7,7 +7,6 @@ export const StyledTab = styled((props: any) => (
fontWeight: theme.typography.fontWeightRegular, fontWeight: theme.typography.fontWeightRegular,
fontSize: theme.typography.pxToRem(15), fontSize: theme.typography.pxToRem(15),
marginRight: theme.spacing(1), marginRight: theme.spacing(1),
color: "#0d6efd",
background: "0 0", background: "0 0",
border: "1px solid transparent", border: "1px solid transparent",
borderTopLeftRadius: "0.25rem", borderTopLeftRadius: "0.25rem",
@ -19,7 +18,7 @@ export const StyledTab = styled((props: any) => (
color: "#495057", color: "#495057",
backgroundColor: "#fff", backgroundColor: "#fff",
borderColor: "#dee2e6 #dee2e6 #fff", borderColor: "#dee2e6 #dee2e6 #fff",
marginBottom: "-2px", marginBottom: "-3px",
}, },
"&.Mui-focusVisible": { "&.Mui-focusVisible": {
backgroundColor: "rgba(100, 95, 228, 0.32)", backgroundColor: "rgba(100, 95, 228, 0.32)",
@ -28,7 +27,6 @@ export const StyledTab = styled((props: any) => (
export const AntTabs = styled(Tabs)({ export const AntTabs = styled(Tabs)({
borderBottom: "1px solid #dee2e6", borderBottom: "1px solid #dee2e6",
overflow: "visible!important", overflow: "visible!important",
"& div.MuiTabs-scroller": { "& div.MuiTabs-scroller": {
overflow: "visible!important", overflow: "visible!important",

View File

@ -1,10 +1,10 @@
// TODO add if required or not // TODO add if required or not
export interface I_Installation { export interface I_Installation {
type: string; type: string;
title: string; title?: string;
status: number; status?: number;
detail: string; detail?: string;
instance: string; instance?: string;
location: string; location: string;
region: string; region: string;
country: string; country: string;
@ -16,6 +16,7 @@ export interface I_Installation {
name: string; name: string;
information: string; information: string;
parentId: number; parentId: number;
s3Url: string;
} }
export interface I_Folder { export interface I_Folder {