[WIP] add users route, change routing structure, change styling of tabs
This commit is contained in:
parent
2a664ac213
commit
52eff388a5
|
@ -1,7 +1,7 @@
|
||||||
import useToken from "./hooks/useToken";
|
import useToken from "./hooks/useToken";
|
||||||
import Login from "./Login";
|
import Login from "./Login";
|
||||||
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
|
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
|
||||||
import { Grid } from "@mui/material";
|
import { Container, Grid } from "@mui/material";
|
||||||
import routes from "./routes.json";
|
import routes from "./routes.json";
|
||||||
import { IntlProvider } from "react-intl";
|
import { IntlProvider } from "react-intl";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
@ -10,21 +10,23 @@ import de from "./lang/de.json";
|
||||||
import Installations from "./components/Installations/Installations";
|
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 Groups from "./components/Groups/Groups";
|
import Users from "./components/Users/Users";
|
||||||
|
import NavigationButtons from "./components/Layout/NavigationButtons";
|
||||||
|
import InstallationPage from "./components/Installations/InstallationPage";
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const { token, setToken, removeToken } = useToken();
|
const { token, setToken, removeToken } = useToken();
|
||||||
const [language, setLanguage] = useState("en");
|
const [language, setLanguage] = useState("EN");
|
||||||
|
|
||||||
const getTranslations = () => {
|
const getTranslations = () => {
|
||||||
if (language === "de") {
|
if (language === "DE") {
|
||||||
return de;
|
return de;
|
||||||
}
|
}
|
||||||
return en;
|
return en;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return <Login setToken={setToken} />;
|
return <Login setToken={setToken} setLanguage={setLanguage} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -32,23 +34,36 @@ const App = () => {
|
||||||
<IntlProvider
|
<IntlProvider
|
||||||
messages={getTranslations()}
|
messages={getTranslations()}
|
||||||
locale={language}
|
locale={language}
|
||||||
defaultLocale="en"
|
defaultLocale="EN"
|
||||||
>
|
>
|
||||||
<Grid container justifyContent="flex-end" sx={{ px: 1, pt: 1 }}>
|
<Container maxWidth="xl" sx={{ margin: 2 }}>
|
||||||
<LanguageSelect language={language} setLanguage={setLanguage} />
|
<Grid container spacing={2}>
|
||||||
<LogoutButton removeToken={removeToken} />
|
<Grid item xs={3}>
|
||||||
</Grid>
|
<NavigationButtons />
|
||||||
<Routes>
|
</Grid>
|
||||||
<Route
|
<Grid
|
||||||
path="*"
|
item
|
||||||
element={<Navigate to={routes.installations} replace />}
|
xs={9}
|
||||||
/>
|
container
|
||||||
<Route
|
justifyContent="flex-end"
|
||||||
path={routes.installations + "*"}
|
sx={{ px: 1, pt: 1 }}
|
||||||
element={<Installations />}
|
>
|
||||||
/>
|
<LanguageSelect language={language} setLanguage={setLanguage} />
|
||||||
<Route path={routes.groups + "*"} element={<Groups />} />
|
<LogoutButton removeToken={removeToken} />
|
||||||
</Routes>
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path="*"
|
||||||
|
element={<Navigate to={routes.installations} replace />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path={routes.installations + "*"}
|
||||||
|
element={<InstallationPage />}
|
||||||
|
/>
|
||||||
|
<Route path={routes.users + "*"} element={<Users />} />
|
||||||
|
</Routes>
|
||||||
|
</Container>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,12 @@ const loginUser = async (username: string, password: string) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const Login = ({ setToken }: { setToken: (value: string) => void }) => {
|
interface I_LoginProps {
|
||||||
|
setToken: (value: string) => void;
|
||||||
|
setLanguage: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Login = ({ setToken, setLanguage }: I_LoginProps) => {
|
||||||
const [username, setUsername] = useState("");
|
const [username, setUsername] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
@ -31,6 +36,7 @@ const Login = ({ setToken }: { setToken: (value: string) => void }) => {
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setToken(data.token);
|
setToken(data.token);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
setLanguage(data.user.language);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setError(err);
|
setError(err);
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import axiosConfig from "../../config/axiosConfig";
|
||||||
|
import { I_User, UserWithInheritedAccess } from "../../util/user.util";
|
||||||
|
import { GroupContext } from "./GroupContextProvider";
|
||||||
|
|
||||||
|
interface UserContextProviderProps {
|
||||||
|
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[];
|
||||||
|
fetchUsersWithInheritedAccessForResource: () => Promise<void>;
|
||||||
|
fetchUsersWithDirectAccessForResource: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserContext = createContext<UserContextProviderProps>({
|
||||||
|
directAccessUsers: [],
|
||||||
|
setDirectAccessUsers: () => {},
|
||||||
|
inheritedAccessUsers: [],
|
||||||
|
setInheritedAccessUsers: () => {},
|
||||||
|
availableUsers: [],
|
||||||
|
setAvailableUsers: () => {},
|
||||||
|
getAvailableUsers: () => {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
fetchUsersWithInheritedAccessForResource: () => {
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
fetchUsersWithDirectAccessForResource: () => {
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const UserContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
|
const [directAccessUsers, setDirectAccessUsers] = useState<I_User[]>([]);
|
||||||
|
const [inheritedAccessUsers, setInheritedAccessUsers] = useState<
|
||||||
|
UserWithInheritedAccess[]
|
||||||
|
>([]);
|
||||||
|
const [availableUsers, setAvailableUsers] = useState<I_User[]>([]);
|
||||||
|
|
||||||
|
const { currentType } = useContext(GroupContext);
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
|
const fetchUsers = useCallback(
|
||||||
|
async (route: string, setState: (value: any) => void) => {
|
||||||
|
axiosConfig.get(route + currentType, { params: { id } }).then((res) => {
|
||||||
|
setState(res.data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[currentType, id]
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchUsersWithInheritedAccessForResource = useCallback(async () => {
|
||||||
|
fetchUsers("/GetUsersWithInheritedAccessTo", setInheritedAccessUsers);
|
||||||
|
}, [fetchUsers]);
|
||||||
|
|
||||||
|
const fetchUsersWithDirectAccessForResource = useCallback(async () => {
|
||||||
|
fetchUsers("/GetUsersWithDirectAccessTo", setDirectAccessUsers);
|
||||||
|
}, [fetchUsers]);
|
||||||
|
|
||||||
|
const getAvailableUsersForResource = () => {
|
||||||
|
const inheritedUsers = inheritedAccessUsers.map(
|
||||||
|
(inheritedAccessUser) => inheritedAccessUser.user
|
||||||
|
);
|
||||||
|
const allUsersWithAccess = [...inheritedUsers, ...directAccessUsers];
|
||||||
|
return availableUsers.filter(
|
||||||
|
(availableUser) =>
|
||||||
|
!allUsersWithAccess.find(
|
||||||
|
(userWithAccess) => availableUser.id === userWithAccess.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
axiosConfig.get("/GetAllChildUsers").then((res) => {
|
||||||
|
setAvailableUsers(res.data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<UserContext.Provider
|
||||||
|
value={{
|
||||||
|
directAccessUsers,
|
||||||
|
setDirectAccessUsers,
|
||||||
|
inheritedAccessUsers,
|
||||||
|
setInheritedAccessUsers,
|
||||||
|
availableUsers,
|
||||||
|
setAvailableUsers,
|
||||||
|
getAvailableUsers: getAvailableUsersForResource,
|
||||||
|
fetchUsersWithInheritedAccessForResource,
|
||||||
|
fetchUsersWithDirectAccessForResource,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</UserContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserContextProvider;
|
|
@ -1,23 +1,24 @@
|
||||||
import { Grid } from "@mui/material";
|
import { Grid } from "@mui/material";
|
||||||
import AvailableUserList from "./AvailableUserList";
|
import UserContextProvider from "../../Context/UserContextProvider";
|
||||||
import InnovenergyList from "./UserList";
|
import AvailableUserDialog from "./AvailableUserDialog";
|
||||||
|
import InnovenergyList from "./InnovenergyList";
|
||||||
import UsersWithDirectAccess from "./UsersWithDirectAccess";
|
import UsersWithDirectAccess from "./UsersWithDirectAccess";
|
||||||
import UsersWithInheritedAccess from "./UsersWithInheritedAccess";
|
import UsersWithInheritedAccess from "./UsersWithInheritedAccess";
|
||||||
|
|
||||||
const Users = () => {
|
const AccessManagement = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<UserContextProvider>
|
||||||
<AvailableUserList />
|
|
||||||
<Grid container sx={{ mt: 1 }}>
|
<Grid container sx={{ mt: 1 }}>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
|
<AvailableUserDialog />
|
||||||
<InnovenergyList>
|
<InnovenergyList>
|
||||||
<UsersWithDirectAccess />
|
<UsersWithDirectAccess />
|
||||||
<UsersWithInheritedAccess />
|
<UsersWithInheritedAccess />
|
||||||
</InnovenergyList>
|
</InnovenergyList>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</>
|
</UserContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Users;
|
export default AccessManagement;
|
|
@ -1,23 +1,32 @@
|
||||||
import { Autocomplete, Button, Grid, TextField } from "@mui/material";
|
import {
|
||||||
import { useContext, useEffect, useState } from "react";
|
Autocomplete,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
TextField,
|
||||||
|
} from "@mui/material";
|
||||||
|
import DialogActions from "@mui/material/DialogActions";
|
||||||
|
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 { 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";
|
||||||
|
|
||||||
const AvailableUserList = () => {
|
const AvailableUserDialog = () => {
|
||||||
const [users, setUsers] = useState<User[]>([]);
|
const [selectedUsers, setSelectedUsers] = useState<I_User[]>([]);
|
||||||
const [selectedUsers, setSelectedUsers] = useState<User[]>([]);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const { currentType } = useContext(GroupContext);
|
const { currentType } = useContext(GroupContext);
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
const {
|
||||||
|
getAvailableUsers,
|
||||||
|
fetchUsersWithDirectAccessForResource,
|
||||||
|
fetchUsersWithInheritedAccessForResource,
|
||||||
|
} = useContext(UserContext);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
axiosConfig.get("/GetAllChildUsers").then((res) => {
|
|
||||||
setUsers(res.data);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
const handleGrant = () => {
|
const handleGrant = () => {
|
||||||
selectedUsers.forEach((user) => {
|
selectedUsers.forEach((user) => {
|
||||||
const bodyProp = currentType === "Folder" ? "folderId" : "installationId";
|
const bodyProp = currentType === "Folder" ? "folderId" : "installationId";
|
||||||
|
@ -29,20 +38,32 @@ const AvailableUserList = () => {
|
||||||
: "/GrantUserAccessToInstallation",
|
: "/GrantUserAccessToInstallation",
|
||||||
{ userId: user.id, [bodyProp]: elementId }
|
{ userId: user.id, [bodyProp]: elementId }
|
||||||
)
|
)
|
||||||
.then((res) => {
|
.then(() => {
|
||||||
console.log(res);
|
fetchUsersWithDirectAccessForResource();
|
||||||
|
fetchUsersWithInheritedAccessForResource();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
if (users) {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid container sx={{ pt: 1 }}>
|
<Dialog
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
aria-labelledby="customized-dialog-title"
|
||||||
|
open={open}
|
||||||
|
sx={{
|
||||||
|
".MuiDialogContent-root": { overflowX: "hidden" },
|
||||||
|
maxHeight: 500,
|
||||||
|
}}
|
||||||
|
scroll="paper"
|
||||||
|
>
|
||||||
|
<DialogTitle>Create new folder</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
sx={{ width: "500px" }}
|
sx={{ width: "500px" }}
|
||||||
multiple
|
multiple
|
||||||
id="tags-standard"
|
id="tags-standard"
|
||||||
options={users}
|
options={getAvailableUsers()}
|
||||||
getOptionLabel={(option) => option.name}
|
getOptionLabel={(option) => option.name}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -54,6 +75,8 @@ const AvailableUserList = () => {
|
||||||
)}
|
)}
|
||||||
onChange={(event, values) => setSelectedUsers(values)}
|
onChange={(event, values) => setSelectedUsers(values)}
|
||||||
/>
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@ -62,11 +85,13 @@ const AvailableUserList = () => {
|
||||||
>
|
>
|
||||||
<FormattedMessage id="grantAccess" defaultMessage="Grant access" />
|
<FormattedMessage id="grantAccess" defaultMessage="Grant access" />
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</DialogActions>
|
||||||
</>
|
</Dialog>
|
||||||
);
|
<Button variant="outlined" type="submit" onClick={() => setOpen(true)}>
|
||||||
}
|
<FormattedMessage id="grantAccess" defaultMessage="Grant access" />
|
||||||
return null;
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AvailableUserList;
|
export default AvailableUserDialog;
|
|
@ -6,31 +6,23 @@ import {
|
||||||
Avatar,
|
Avatar,
|
||||||
ListItemAvatar,
|
ListItemAvatar,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { Fragment, useCallback, useContext, useEffect, useState } from "react";
|
import { Fragment, useContext, useEffect } from "react";
|
||||||
import axiosConfig from "../../../config/axiosConfig";
|
import axiosConfig from "../../../config/axiosConfig";
|
||||||
import { User } from "../../../util/user.util";
|
|
||||||
import PersonRemoveIcon from "@mui/icons-material/PersonRemove";
|
import PersonRemoveIcon from "@mui/icons-material/PersonRemove";
|
||||||
import { GroupContext } from "../../Context/GroupContextProvider";
|
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import PersonIcon from "@mui/icons-material/Person";
|
import PersonIcon from "@mui/icons-material/Person";
|
||||||
|
import { UserContext } from "../../Context/UserContextProvider";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { GroupContext } from "../../Context/GroupContextProvider";
|
||||||
|
|
||||||
const UsersWithDirectAccess = () => {
|
const UsersWithDirectAccess = () => {
|
||||||
const [users, setUsers] = useState<User[]>();
|
const { fetchUsersWithDirectAccessForResource, directAccessUsers } =
|
||||||
|
useContext(UserContext);
|
||||||
const { currentType } = useContext(GroupContext);
|
const { currentType } = useContext(GroupContext);
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
||||||
const fetchUsers = useCallback(async () => {
|
|
||||||
axiosConfig
|
|
||||||
.get("/GetUsersWithDirectAccessTo" + currentType, { params: { id } })
|
|
||||||
.then((res) => {
|
|
||||||
setUsers(res.data);
|
|
||||||
});
|
|
||||||
}, [currentType, id]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchUsers();
|
fetchUsersWithDirectAccessForResource();
|
||||||
}, [fetchUsers]);
|
}, [fetchUsersWithDirectAccessForResource]);
|
||||||
|
|
||||||
const handleIconClick = (userId: number) => {
|
const handleIconClick = (userId: number) => {
|
||||||
const folderId = id ? parseInt(id) : "";
|
const folderId = id ? parseInt(id) : "";
|
||||||
|
@ -40,14 +32,14 @@ const UsersWithDirectAccess = () => {
|
||||||
folderId: folderId,
|
folderId: folderId,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
fetchUsers();
|
fetchUsersWithDirectAccessForResource();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (users && users.length) {
|
if (directAccessUsers && directAccessUsers.length) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{users.map((user) => {
|
{directAccessUsers.map((user) => {
|
||||||
return (
|
return (
|
||||||
<Fragment key={user.id}>
|
<Fragment key={user.id}>
|
||||||
<ListItem
|
<ListItem
|
|
@ -5,39 +5,28 @@ import {
|
||||||
ListItemAvatar,
|
ListItemAvatar,
|
||||||
Avatar,
|
Avatar,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { Fragment, useCallback, useContext, useEffect, useState } from "react";
|
import { Fragment, useContext, useEffect } from "react";
|
||||||
import { Link, useParams } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import axiosConfig from "../../../config/axiosConfig";
|
|
||||||
import {
|
import {
|
||||||
filterDuplicateUsers,
|
filterDuplicateUsers,
|
||||||
UserWithInheritedAccess,
|
UserWithInheritedAccess,
|
||||||
} from "../../../util/user.util";
|
} from "../../../util/user.util";
|
||||||
import { GroupContext } from "../../Context/GroupContextProvider";
|
|
||||||
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";
|
||||||
|
|
||||||
const UsersWithInheritedAccess = () => {
|
const UsersWithInheritedAccess = () => {
|
||||||
const [users, setUsers] = useState<UserWithInheritedAccess[]>();
|
const { fetchUsersWithInheritedAccessForResource, inheritedAccessUsers } =
|
||||||
|
useContext(UserContext);
|
||||||
const { currentType } = useContext(GroupContext);
|
|
||||||
const { id } = useParams();
|
|
||||||
|
|
||||||
const fetchUsers = useCallback(async () => {
|
|
||||||
axiosConfig
|
|
||||||
.get("/GetUsersWithInheritedAccessTo" + currentType, { params: { id } })
|
|
||||||
.then((res) => {
|
|
||||||
setUsers(res.data);
|
|
||||||
});
|
|
||||||
}, [currentType, id]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchUsers();
|
fetchUsersWithInheritedAccessForResource();
|
||||||
}, [fetchUsers]);
|
}, [fetchUsersWithInheritedAccessForResource]);
|
||||||
|
|
||||||
if (users && users.length) {
|
if (inheritedAccessUsers && inheritedAccessUsers.length) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{filterDuplicateUsers(users).map(
|
{filterDuplicateUsers(inheritedAccessUsers).map(
|
||||||
({ user, folderName }: UserWithInheritedAccess) => {
|
({ user, folderName }: UserWithInheritedAccess) => {
|
||||||
return (
|
return (
|
||||||
<Fragment key={user.id}>
|
<Fragment key={user.id}>
|
||||||
|
@ -52,7 +41,11 @@ const UsersWithInheritedAccess = () => {
|
||||||
secondary={
|
secondary={
|
||||||
<>
|
<>
|
||||||
Inherited access from{" "}
|
Inherited access from{" "}
|
||||||
<Link to={routes.groups + routes.users + user.parentId}>
|
<Link
|
||||||
|
to={
|
||||||
|
routes.groups + routes.manageAccess + user.parentId
|
||||||
|
}
|
||||||
|
>
|
||||||
{folderName}
|
{folderName}
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { Button, Dialog, DialogContent, DialogTitle } 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";
|
||||||
|
|
||||||
|
interface AddNewDialogProps {
|
||||||
|
values: I_Folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddNewDialog = (props: AddNewDialogProps) => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = (data: I_Folder, childData: Partial<I_Folder>) => {
|
||||||
|
return axiosConfig.post("/CreateFolder", {
|
||||||
|
...childData,
|
||||||
|
parentId: data.id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button variant="outlined" sx={{ ml: 2 }} onClick={() => setOpen(true)}>
|
||||||
|
<FormattedMessage id="addNewChild" defaultMessage="Add new child" />
|
||||||
|
</Button>
|
||||||
|
<Dialog
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
aria-labelledby="customized-dialog-title"
|
||||||
|
open={open}
|
||||||
|
sx={{
|
||||||
|
".MuiDialogContent-root": { overflowX: "hidden" },
|
||||||
|
maxHeight: 500,
|
||||||
|
}}
|
||||||
|
scroll="paper"
|
||||||
|
>
|
||||||
|
<DialogTitle>Create new folder</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<FolderForm values={props.values} handleSubmit={handleSubmit} />
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddNewDialog;
|
|
@ -3,12 +3,14 @@ import { AxiosError } from "axios";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import axiosConfig from "../../config/axiosConfig";
|
||||||
import { I_Installation } from "../../util/types";
|
import { I_Folder } from "../../util/types";
|
||||||
|
import AddNewDialog from "./AddNewDialog";
|
||||||
import FolderForm from "./FolderForm";
|
import FolderForm from "./FolderForm";
|
||||||
|
import MoveDialog from "./Tree/MoveDialog";
|
||||||
|
|
||||||
const Folder = () => {
|
const Folder = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [values, setValues] = useState<I_Installation>();
|
const [values, setValues] = useState<I_Folder>();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<AxiosError>();
|
const [error, setError] = useState<AxiosError>();
|
||||||
|
|
||||||
|
@ -26,11 +28,22 @@ const Folder = () => {
|
||||||
});
|
});
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
|
const handleSubmit = (data: I_Folder, formikValues: Partial<I_Folder>) => {
|
||||||
|
return axiosConfig.put("/UpdateFolder", { ...data, ...formikValues });
|
||||||
|
};
|
||||||
|
|
||||||
if (values && values.id && values.id.toString() === id) {
|
if (values && values.id && values.id.toString() === id) {
|
||||||
|
const moveDialog = <MoveDialog values={values} />;
|
||||||
|
const addNewDialog = <AddNewDialog values={values} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box sx={{ py: 3 }}>
|
<Box sx={{ py: 3 }}>
|
||||||
<FolderForm values={values} id={id} />
|
<FolderForm
|
||||||
|
values={values}
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
additionalButtons={[moveDialog, addNewDialog]}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,25 +1,24 @@
|
||||||
import { Button, CircularProgress, Grid } from "@mui/material";
|
import { Button, CircularProgress, Grid } from "@mui/material";
|
||||||
|
import { AxiosResponse } from "axios";
|
||||||
import { useFormik } from "formik";
|
import { useFormik } from "formik";
|
||||||
import { useContext, useState } from "react";
|
import { ReactNode, useContext, useState } from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
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 InnovenergySnackbar from "../InnovenergySnackbar";
|
import InnovenergySnackbar from "../InnovenergySnackbar";
|
||||||
import InnovenergyTextfield from "../Layout/InnovenergyTextfield";
|
import InnovenergyTextfield from "../Layout/InnovenergyTextfield";
|
||||||
import MoveDialog from "./Tree/MoveDialog";
|
|
||||||
|
|
||||||
interface I_CustomerFormProps {
|
interface I_CustomerFormProps {
|
||||||
values: I_Folder;
|
values: I_Folder;
|
||||||
id: string;
|
handleSubmit: (
|
||||||
|
data: I_Folder,
|
||||||
|
formikValues: Partial<I_Folder>
|
||||||
|
) => Promise<AxiosResponse>;
|
||||||
|
additionalButtons?: ReactNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateFolder = (data: I_Folder) => {
|
|
||||||
return axiosConfig.put("/UpdateFolder", data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const FolderForm = (props: I_CustomerFormProps) => {
|
const FolderForm = (props: I_CustomerFormProps) => {
|
||||||
const { values, id } = props;
|
const { values, additionalButtons, handleSubmit } = props;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { fetchData } = useContext(GroupContext);
|
const { fetchData } = useContext(GroupContext);
|
||||||
|
|
||||||
|
@ -34,14 +33,8 @@ const FolderForm = (props: I_CustomerFormProps) => {
|
||||||
},
|
},
|
||||||
onSubmit: (formikValues) => {
|
onSubmit: (formikValues) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const idAsNumber = parseInt(id, 10);
|
handleSubmit(values, formikValues)
|
||||||
updateFolder({
|
.then(() => {
|
||||||
...values,
|
|
||||||
...formikValues,
|
|
||||||
id: idAsNumber,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
// TODO figure out why this isnt refreshing tree
|
|
||||||
fetchData();
|
fetchData();
|
||||||
setSnackbarOpen(true);
|
setSnackbarOpen(true);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
@ -79,12 +72,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 />}
|
||||||
<MoveDialog values={values} />
|
{additionalButtons && additionalButtons.map((button) => button)}
|
||||||
<Button variant="outlined" type="submit" sx={{ ml: 2 }}>
|
<Button variant="outlined" type="submit" sx={{ ml: 2 }}>
|
||||||
<FormattedMessage
|
<FormattedMessage id="submit" defaultMessage="Submit" />
|
||||||
id="applyChanges"
|
|
||||||
defaultMessage="Apply changes"
|
|
||||||
/>
|
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<InnovenergySnackbar
|
<InnovenergySnackbar
|
||||||
|
|
|
@ -1,34 +1,33 @@
|
||||||
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 { GroupContext } from "../Context/GroupContextProvider";
|
import { GroupContext } from "../Context/GroupContextProvider";
|
||||||
|
import { useContext } from "react";
|
||||||
|
import { AntTabs, StyledTab } from "../../util/installation.util";
|
||||||
|
|
||||||
const GroupTabs = () => {
|
const GroupTabs = () => {
|
||||||
const routeMatch = useRouteMatch([
|
const routeMatch = useRouteMatch([
|
||||||
routes.groups + routes.folder + ":id",
|
routes.groups + routes.folder + ":id",
|
||||||
routes.groups + routes.users + ":id",
|
routes.groups + routes.manageAccess + ":id",
|
||||||
routes.groups + routes.installation + ":id",
|
routes.groups + routes.installation + ":id",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const id = routeMatch?.params?.id;
|
const id = routeMatch?.params?.id;
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { currentType } = React.useContext(GroupContext);
|
const { currentType } = useContext(GroupContext);
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
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}
|
value={routeMatch?.pattern?.path}
|
||||||
aria-label="basic tabs example"
|
aria-label="basic tabs example"
|
||||||
>
|
>
|
||||||
{currentType === "Folder" ? (
|
{currentType === "Folder" ? (
|
||||||
<Tab
|
<StyledTab
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: "folder",
|
id: "folder",
|
||||||
defaultMessage: "Folder",
|
defaultMessage: "Folder",
|
||||||
|
@ -38,7 +37,7 @@ const GroupTabs = () => {
|
||||||
to={routes.folder + id}
|
to={routes.folder + id}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Tab
|
<StyledTab
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: "installation",
|
id: "installation",
|
||||||
defaultMessage: "Installation",
|
defaultMessage: "Installation",
|
||||||
|
@ -49,16 +48,16 @@ const GroupTabs = () => {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Tab
|
<StyledTab
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: "users",
|
id: "manageAccess",
|
||||||
defaultMessage: "Users",
|
defaultMessage: "Manage access",
|
||||||
})}
|
})}
|
||||||
value={routes.groups + routes.users + ":id"}
|
value={routes.groups + routes.manageAccess + ":id"}
|
||||||
component={Link}
|
component={Link}
|
||||||
to={routes.users + id}
|
to={routes.manageAccess + id}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</AntTabs>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,36 +2,43 @@ import { Grid } from "@mui/material";
|
||||||
import { Container } from "@mui/system";
|
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 Installation from "../Installations/Installation";
|
|
||||||
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 NavigationButtons from "../Layout/NavigationButtons";
|
||||||
import Users from "./Users/Users";
|
import AccessManagement from "./AccessManagement/AccessManagement";
|
||||||
|
import { I_Installation } from "../../util/types";
|
||||||
|
import InstallationForm from "../Installations/InstallationForm";
|
||||||
|
import Detail from "../Layout/Detail";
|
||||||
|
|
||||||
const Groups = () => {
|
const Groups = () => {
|
||||||
return (
|
return (
|
||||||
<GroupContextProvider>
|
<GroupContextProvider>
|
||||||
<Container maxWidth="xl">
|
<Grid container spacing={2}>
|
||||||
<Grid container spacing={2}>
|
<Grid item xs={3}>
|
||||||
<Grid item xs={3}>
|
<GroupTree />
|
||||||
<NavigationButtons />
|
|
||||||
<GroupTree />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={9}>
|
|
||||||
<GroupTabs />
|
|
||||||
<Routes>
|
|
||||||
<Route path={routes.folder + ":id"} element={<Folder />} index />
|
|
||||||
<Route path={routes.users + ":id"} element={<Users />} />
|
|
||||||
<Route
|
|
||||||
path={routes.installation + ":id"}
|
|
||||||
element={<Installation />}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
<Grid item xs={9}>
|
||||||
|
<GroupTabs />
|
||||||
|
<Routes>
|
||||||
|
<Route path={routes.folder + ":id"} element={<Folder />} index />
|
||||||
|
<Route
|
||||||
|
path={routes.manageAccess + ":id"}
|
||||||
|
element={<AccessManagement />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path={routes.installation + ":id"}
|
||||||
|
element={
|
||||||
|
<Detail<I_Installation>
|
||||||
|
route="/GetInstallationById?id="
|
||||||
|
formComponent={InstallationForm}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</GroupContextProvider>
|
</GroupContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,13 +4,14 @@ import { 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 MoveDialog from "../Groups/Tree/MoveDialog";
|
||||||
import InnovenergyTextfield from "../Layout/InnovenergyTextfield";
|
import InnovenergyTextfield from "../Layout/InnovenergyTextfield";
|
||||||
|
|
||||||
interface I_CustomerFormProps {
|
interface I_InstallationFormProps {
|
||||||
values: I_Installation;
|
values: I_Installation;
|
||||||
id: string | undefined;
|
id: string | undefined;
|
||||||
}
|
}
|
||||||
const CustomerForm = (props: I_CustomerFormProps) => {
|
const InstallationForm = (props: I_InstallationFormProps) => {
|
||||||
const { values, id } = props;
|
const { values, id } = props;
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
@ -93,6 +94,7 @@ const CustomerForm = (props: I_CustomerFormProps) => {
|
||||||
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} />
|
||||||
<Button variant="outlined" type="submit">
|
<Button variant="outlined" type="submit">
|
||||||
<FormattedMessage id="applyChanges" defaultMessage="Apply changes" />
|
<FormattedMessage id="applyChanges" defaultMessage="Apply changes" />
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -117,4 +119,4 @@ const CustomerForm = (props: I_CustomerFormProps) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CustomerForm;
|
export default InstallationForm;
|
|
@ -43,7 +43,6 @@ const InstallationList = (props: InstallationListProps) => {
|
||||||
const routeMatch = useRouteMatch([
|
const routeMatch = useRouteMatch([
|
||||||
routes.installations + routes.installation + ":id",
|
routes.installations + routes.installation + ":id",
|
||||||
routes.installations + routes.alarms + ":id",
|
routes.installations + routes.alarms + ":id",
|
||||||
routes.installations + routes.users + ":id",
|
|
||||||
routes.installations + routes.log + ":id",
|
routes.installations + routes.log + ":id",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { Route, Routes } from "react-router-dom";
|
||||||
|
import Groups from "../Groups/Groups";
|
||||||
|
import ModeButtons from "../Layout/ModeButtons";
|
||||||
|
import Installations from "./Installations";
|
||||||
|
import routes from "../../routes.json";
|
||||||
|
import { Grid } from "@mui/material";
|
||||||
|
|
||||||
|
const InstallationPage = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item xs={3}>
|
||||||
|
<ModeButtons />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Routes>
|
||||||
|
<Route path={routes.tree + "*"} element={<Groups />} />
|
||||||
|
<Route path={routes.list + "*"} element={<Installations />} />
|
||||||
|
</Routes>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default InstallationPage;
|
|
@ -2,17 +2,18 @@ import * as React from "react";
|
||||||
import Tabs from "@mui/material/Tabs";
|
import Tabs from "@mui/material/Tabs";
|
||||||
import Tab from "@mui/material/Tab";
|
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, Routes } 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";
|
||||||
|
|
||||||
const InstallationTabs = () => {
|
const InstallationTabs = () => {
|
||||||
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.users + ":id",
|
routes.installations + routes.list + routes.log + ":id",
|
||||||
routes.installations + routes.log + ":id",
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const id = routeMatch?.params?.id;
|
const id = routeMatch?.params?.id;
|
||||||
|
@ -22,47 +23,40 @@ const InstallationTabs = () => {
|
||||||
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.installation + ":id"}
|
value={routeMatch?.pattern?.path ?? routes.installation + ":id"}
|
||||||
aria-label="basic tabs example"
|
aria-label="basic tabs example"
|
||||||
>
|
>
|
||||||
<Tab
|
<StyledTab
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: "installation",
|
id: "installation",
|
||||||
defaultMessage: "Installation",
|
defaultMessage: "Installation",
|
||||||
})}
|
})}
|
||||||
value={routes.installations + routes.installation + ":id"}
|
value={
|
||||||
|
routes.installations + routes.list + routes.installation + ":id"
|
||||||
|
}
|
||||||
component={Link}
|
component={Link}
|
||||||
to={routes.installation + id}
|
to={routes.installation + id}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<StyledTab
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: "alarms",
|
id: "alarms",
|
||||||
defaultMessage: "Alarms",
|
defaultMessage: "Alarms",
|
||||||
})}
|
})}
|
||||||
value={routes.installations + routes.alarms + ":id"}
|
value={routes.installations + routes.list + routes.alarms + ":id"}
|
||||||
component={Link}
|
component={Link}
|
||||||
to={routes.alarms + id}
|
to={routes.alarms + id}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<StyledTab
|
||||||
label={intl.formatMessage({
|
|
||||||
id: "users",
|
|
||||||
defaultMessage: "Users",
|
|
||||||
})}
|
|
||||||
value={routes.installations + routes.users + ":id"}
|
|
||||||
component={Link}
|
|
||||||
to={routes.users + id}
|
|
||||||
/>
|
|
||||||
<Tab
|
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
id: "log",
|
id: "log",
|
||||||
defaultMessage: "Log",
|
defaultMessage: "Log",
|
||||||
})}
|
})}
|
||||||
value={routes.installations + routes.log + ":id"}
|
value={routes.installations + routes.list + routes.log + ":id"}
|
||||||
component={Link}
|
component={Link}
|
||||||
to={routes.log + id}
|
to={routes.log + id}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</AntTabs>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,38 +3,40 @@ import { Container } from "@mui/system";
|
||||||
import { Routes, Route } from "react-router";
|
import { Routes, Route } from "react-router";
|
||||||
import NavigationButtons from "../Layout/NavigationButtons";
|
import NavigationButtons from "../Layout/NavigationButtons";
|
||||||
import Sidebar from "../Layout/Sidebar";
|
import Sidebar from "../Layout/Sidebar";
|
||||||
import BasicTable from "../Layout/Table";
|
|
||||||
import Alarms from "./Alarms";
|
import Alarms from "./Alarms";
|
||||||
import Installation from "./Installation";
|
|
||||||
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 { I_Installation } from "../../util/types";
|
||||||
|
import InstallationForm from "./InstallationForm";
|
||||||
|
|
||||||
const Installations = () => {
|
const Installations = () => {
|
||||||
return (
|
return (
|
||||||
<InstallationContextProvider>
|
<InstallationContextProvider>
|
||||||
<Container maxWidth="xl">
|
<Grid container spacing={2}>
|
||||||
<Grid container spacing={4}>
|
<Grid item xs={3}>
|
||||||
<Grid item xs={3}>
|
<Sidebar />
|
||||||
<NavigationButtons />
|
|
||||||
<Sidebar />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={9}>
|
|
||||||
<InstallationTabs />
|
|
||||||
<Routes>
|
|
||||||
<Route
|
|
||||||
path={routes.installation + ":id"}
|
|
||||||
element={<Installation />}
|
|
||||||
index
|
|
||||||
/>
|
|
||||||
<Route path={routes.alarms + ":id"} element={<Alarms />} />
|
|
||||||
<Route path={routes.users + ":id"} element={<BasicTable />} />
|
|
||||||
<Route path={routes.log + ":id"} element={<Log />} />
|
|
||||||
</Routes>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
<Grid item xs={9}>
|
||||||
|
<InstallationTabs />
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path={routes.installation + ":id"}
|
||||||
|
element={
|
||||||
|
<Detail<I_Installation>
|
||||||
|
route="/GetInstallationById?id="
|
||||||
|
formComponent={InstallationForm}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
index
|
||||||
|
/>
|
||||||
|
<Route path={routes.alarms + ":id"} element={<Alarms />} />
|
||||||
|
<Route path={routes.log + ":id"} element={<Log />} />
|
||||||
|
</Routes>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</InstallationContextProvider>
|
</InstallationContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +1,27 @@
|
||||||
import { Alert, Box, CircularProgress } from "@mui/material";
|
import { Box, CircularProgress, Alert } from "@mui/material";
|
||||||
import { AxiosError } from "axios";
|
import { AxiosError } from "axios";
|
||||||
import { useEffect, useState } from "react";
|
import { useState, useEffect, FC } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import CustomerForm from "./CustomerForm";
|
|
||||||
import axiosConfig from "../../config/axiosConfig";
|
import axiosConfig from "../../config/axiosConfig";
|
||||||
import { I_Installation } from "../../util/types";
|
export interface I_FormProps<T> {
|
||||||
import LocationForm from "../Groups/LocationForm";
|
values: T;
|
||||||
|
id: string;
|
||||||
const Installation = () => {
|
}
|
||||||
|
interface I_DetailProps<T> {
|
||||||
|
formComponent: FC<I_FormProps<T>>;
|
||||||
|
route: string;
|
||||||
|
}
|
||||||
|
const Detail = <T extends { id: number }>(props: I_DetailProps<T>) => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [values, setValues] = useState<I_Installation>();
|
const { formComponent: FormComponent, route } = props;
|
||||||
|
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>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
axiosConfig
|
axiosConfig
|
||||||
.get("/GetInstallationById?id=" + id)
|
.get(route + id)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setValues(res.data);
|
setValues(res.data);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
@ -25,15 +30,10 @@ const Installation = () => {
|
||||||
setError(err);
|
setError(err);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}, [id]);
|
}, [id, route]);
|
||||||
|
|
||||||
if (values && values.id && values.id.toString() === id) {
|
if (values && values.id && values.id.toString() === id) {
|
||||||
return (
|
return <FormComponent values={values} id={id} />;
|
||||||
<Box sx={{ py: 3 }}>
|
|
||||||
<CustomerForm values={values} id={id} />
|
|
||||||
<LocationForm parentId={values.parentId} />
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
} else if (loading) {
|
} else if (loading) {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
|
@ -52,4 +52,4 @@ const Installation = () => {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Installation;
|
export default Detail;
|
|
@ -14,10 +14,10 @@ const LanguageSelect = (props: LanguageSelectProps) => {
|
||||||
label="Age"
|
label="Age"
|
||||||
onChange={(e) => props.setLanguage(e.target.value)}
|
onChange={(e) => props.setLanguage(e.target.value)}
|
||||||
>
|
>
|
||||||
<MenuItem value="en">
|
<MenuItem value="EN">
|
||||||
<FormattedMessage id="english" defaultMessage="English" />
|
<FormattedMessage id="english" defaultMessage="English" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem value="de">
|
<MenuItem value="DE">
|
||||||
<FormattedMessage id="german" defaultMessage="German" />
|
<FormattedMessage id="german" defaultMessage="German" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
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";
|
||||||
|
|
||||||
|
const ModeButtons = () => {
|
||||||
|
const routeMatch = useRouteMatch([
|
||||||
|
routes.installations + routes.tree + "*",
|
||||||
|
routes.installations + routes.list + "*",
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AntTabs
|
||||||
|
value={routeMatch?.pattern?.path ?? routes.installation + ":id"}
|
||||||
|
aria-label="basic tabs example"
|
||||||
|
>
|
||||||
|
<StyledTab
|
||||||
|
icon={<ListIcon />}
|
||||||
|
value={routes.installations + routes.list + "*"}
|
||||||
|
component={Link}
|
||||||
|
to={routes.list}
|
||||||
|
/>
|
||||||
|
<StyledTab
|
||||||
|
icon={<AccountTreeIcon />}
|
||||||
|
value={routes.installations + routes.tree + "*"}
|
||||||
|
component={Link}
|
||||||
|
to={routes.tree}
|
||||||
|
/>
|
||||||
|
</AntTabs>
|
||||||
|
{/* <ToggleButtonGroup
|
||||||
|
color="primary"
|
||||||
|
value={routeMatch?.pattern?.path}
|
||||||
|
exclusive
|
||||||
|
sx={{ mb: 1 }}
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<ToggleButton
|
||||||
|
value={routes.installations + routes.list + "*"}
|
||||||
|
component={Link}
|
||||||
|
to={routes.list}
|
||||||
|
>
|
||||||
|
<ListIcon />
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton
|
||||||
|
value={routes.installations + routes.tree + "*"}
|
||||||
|
component={Link}
|
||||||
|
to={routes.tree}
|
||||||
|
>
|
||||||
|
<AccountTreeIcon />
|
||||||
|
</ToggleButton>
|
||||||
|
</ToggleButtonGroup> */}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModeButtons;
|
|
@ -7,7 +7,7 @@ import routes from "../../routes.json";
|
||||||
const NavigationButtons = () => {
|
const NavigationButtons = () => {
|
||||||
const routeMatch = useRouteMatch([
|
const routeMatch = useRouteMatch([
|
||||||
routes.installations + "*",
|
routes.installations + "*",
|
||||||
routes.groups + "*",
|
routes.users + "*",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -16,23 +16,21 @@ const NavigationButtons = () => {
|
||||||
value={routeMatch?.pattern?.path}
|
value={routeMatch?.pattern?.path}
|
||||||
exclusive
|
exclusive
|
||||||
sx={{ mb: 1 }}
|
sx={{ mb: 1 }}
|
||||||
|
fullWidth
|
||||||
>
|
>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
value={routes.installations + "*"}
|
value={routes.installations + "*"}
|
||||||
component={Link}
|
component={Link}
|
||||||
to={routes.installations}
|
to={routes.installations}
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage id="installations" defaultMessage="Installations" />
|
||||||
id="allInstallations"
|
|
||||||
defaultMessage="All installations"
|
|
||||||
/>
|
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
value={routes.groups + "*"}
|
value={routes.users + "*"}
|
||||||
component={Link}
|
component={Link}
|
||||||
to={routes.groups}
|
to={routes.users}
|
||||||
>
|
>
|
||||||
<FormattedMessage id="groups" defaultMessage="Groups" />
|
<FormattedMessage id="users" defaultMessage="Users" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</ToggleButtonGroup>
|
</ToggleButtonGroup>
|
||||||
);
|
);
|
||||||
|
|
|
@ -20,6 +20,7 @@ const Sidebar = () => {
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InstallationList searchQuery={searchQuery} />
|
<InstallationList searchQuery={searchQuery} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { Alert, Button, Grid, Snackbar } from "@mui/material";
|
||||||
|
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 InnovenergyTextfield from "../Layout/InnovenergyTextfield";
|
||||||
|
|
||||||
|
interface I_UserFormProps {
|
||||||
|
values: I_User;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
const UserForm = (props: I_UserFormProps) => {
|
||||||
|
const { values, id } = props;
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const formik = useFormik({
|
||||||
|
initialValues: {
|
||||||
|
email: values.email,
|
||||||
|
information: values.information,
|
||||||
|
},
|
||||||
|
onSubmit: (formikValues) => {
|
||||||
|
axiosConfig
|
||||||
|
.put("/UpdateUser", {
|
||||||
|
...formikValues,
|
||||||
|
id: id,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
setOpen(true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={formik.handleSubmit}>
|
||||||
|
<InnovenergyTextfield
|
||||||
|
id="name-textfield"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: "email",
|
||||||
|
defaultMessage: "Email",
|
||||||
|
})}
|
||||||
|
name="email"
|
||||||
|
value={formik.values.email}
|
||||||
|
handleChange={formik.handleChange}
|
||||||
|
/>
|
||||||
|
<InnovenergyTextfield
|
||||||
|
id="region-textfield"
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: "Information",
|
||||||
|
defaultMessage: "Information",
|
||||||
|
})}
|
||||||
|
name="information"
|
||||||
|
value={formik.values.information}
|
||||||
|
handleChange={formik.handleChange}
|
||||||
|
/>
|
||||||
|
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
|
||||||
|
<Button variant="outlined" type="submit">
|
||||||
|
<FormattedMessage id="applyChanges" defaultMessage="Apply changes" />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Snackbar
|
||||||
|
open={open}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: "top",
|
||||||
|
horizontal: "center",
|
||||||
|
}}
|
||||||
|
autoHideDuration={6000}
|
||||||
|
onClose={handleClose}
|
||||||
|
>
|
||||||
|
<Alert onClose={handleClose} severity="success" sx={{ width: "100%" }}>
|
||||||
|
<FormattedMessage
|
||||||
|
id="updatedSuccessfully"
|
||||||
|
defaultMessage="Updated successfully"
|
||||||
|
/>
|
||||||
|
</Alert>
|
||||||
|
</Snackbar>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserForm;
|
|
@ -0,0 +1,77 @@
|
||||||
|
import List from "@mui/material/List";
|
||||||
|
import ListItemButton from "@mui/material/ListItemButton";
|
||||||
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
|
import { Divider } from "@mui/material";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import useRouteMatch from "../../hooks/useRouteMatch";
|
||||||
|
import routes from "../../routes.json";
|
||||||
|
import { Fragment, useContext } from "react";
|
||||||
|
import { I_Installation } from "../../util/types";
|
||||||
|
import { UserContext } from "../Context/UserContextProvider";
|
||||||
|
|
||||||
|
const getPathWithoutId = (path?: string) => {
|
||||||
|
if (path) {
|
||||||
|
const splitString = path.split(":");
|
||||||
|
return splitString[0];
|
||||||
|
}
|
||||||
|
return routes.user;
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterData = (
|
||||||
|
searchQuery: string,
|
||||||
|
data: I_Installation[] | undefined
|
||||||
|
) => {
|
||||||
|
if (data) {
|
||||||
|
return data.filter(
|
||||||
|
(installation) =>
|
||||||
|
installation.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
installation.location.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const UserList = () => {
|
||||||
|
const { availableUsers } = useContext(UserContext);
|
||||||
|
|
||||||
|
const routeMatch = useRouteMatch([routes.users + routes.user + ":id"]);
|
||||||
|
|
||||||
|
if (availableUsers && availableUsers.length) {
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
position: "relative",
|
||||||
|
overflow: "auto",
|
||||||
|
maxHeight: 400,
|
||||||
|
py: 0,
|
||||||
|
mt: 1,
|
||||||
|
}}
|
||||||
|
component="nav"
|
||||||
|
aria-labelledby="nested-list-subheader"
|
||||||
|
>
|
||||||
|
{availableUsers.map((user) => {
|
||||||
|
return (
|
||||||
|
<Fragment key={user.id}>
|
||||||
|
<Link
|
||||||
|
to={getPathWithoutId(routeMatch?.pattern?.path) + user.id}
|
||||||
|
style={{ textDecoration: "none", color: "black" }}
|
||||||
|
>
|
||||||
|
<ListItemButton
|
||||||
|
selected={user.id === Number(routeMatch?.params.id)}
|
||||||
|
>
|
||||||
|
<ListItemText primary={user.name} />
|
||||||
|
</ListItemButton>
|
||||||
|
</Link>
|
||||||
|
<Divider />
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserList;
|
|
@ -0,0 +1,41 @@
|
||||||
|
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 } from "react-router-dom";
|
||||||
|
import routes from "../../routes.json";
|
||||||
|
import useRouteMatch from "../../hooks/useRouteMatch";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
const UserTabs = () => {
|
||||||
|
const routeMatch = useRouteMatch([routes.users + routes.user + ":id"]);
|
||||||
|
|
||||||
|
const id = routeMatch?.params?.id;
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ width: "100%" }}>
|
||||||
|
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
|
||||||
|
<Tabs
|
||||||
|
value={routeMatch?.pattern?.path ?? routes.user + ":id"}
|
||||||
|
aria-label="basic tabs example"
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
label={intl.formatMessage({
|
||||||
|
id: "user",
|
||||||
|
defaultMessage: "User",
|
||||||
|
})}
|
||||||
|
value={routes.users + routes.user + ":id"}
|
||||||
|
component={Link}
|
||||||
|
to={routes.user + id}
|
||||||
|
/>
|
||||||
|
</Tabs>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserTabs;
|
|
@ -0,0 +1,39 @@
|
||||||
|
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 UserList from "./UserList";
|
||||||
|
import UserTabs from "./UserTabs";
|
||||||
|
|
||||||
|
const Users = () => {
|
||||||
|
return (
|
||||||
|
<UserContextProvider>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item xs={3}>
|
||||||
|
<UserList />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={9}>
|
||||||
|
<UserTabs />
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path={routes.user + ":id"}
|
||||||
|
element={
|
||||||
|
<Detail<I_User>
|
||||||
|
route="/GetUserById?id="
|
||||||
|
formComponent={UserForm}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
index
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</UserContextProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Users;
|
|
@ -1,10 +1,14 @@
|
||||||
{
|
{
|
||||||
"installation": "installation/",
|
"installation": "installation/",
|
||||||
"alarms": "alarms/",
|
"alarms": "alarms/",
|
||||||
"users": "users/",
|
"users": "/users/",
|
||||||
"log": "log/",
|
"log": "log/",
|
||||||
"installations": "/installations/",
|
"installations": "/installations/",
|
||||||
"groups": "/groups/",
|
"groups": "/groups/",
|
||||||
"group": "group/",
|
"group": "group/",
|
||||||
"folder": "folder/"
|
"folder": "folder/",
|
||||||
|
"manageAccess": "manageAccess/",
|
||||||
|
"user": "user/",
|
||||||
|
"tree": "tree/",
|
||||||
|
"list": "list/"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { styled, Tab, Tabs } from "@mui/material";
|
||||||
|
|
||||||
|
export const StyledTab = styled((props: any) => (
|
||||||
|
<Tab disableRipple {...props} />
|
||||||
|
))(({ theme }) => ({
|
||||||
|
textTransform: "none",
|
||||||
|
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",
|
||||||
|
borderTopRightRadius: "0.25rem",
|
||||||
|
padding: ".5rem 1rem",
|
||||||
|
textDecoration: "none",
|
||||||
|
transition: `color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out`,
|
||||||
|
"&.Mui-selected": {
|
||||||
|
color: "#495057",
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
borderColor: "#dee2e6 #dee2e6 #fff",
|
||||||
|
marginBottom: "-2px",
|
||||||
|
},
|
||||||
|
"&.Mui-focusVisible": {
|
||||||
|
backgroundColor: "rgba(100, 95, 228, 0.32)",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const AntTabs = styled(Tabs)({
|
||||||
|
borderBottom: "1px solid #dee2e6",
|
||||||
|
|
||||||
|
overflow: "visible!important",
|
||||||
|
"& div.MuiTabs-scroller": {
|
||||||
|
overflow: "visible!important",
|
||||||
|
},
|
||||||
|
"&.Mui-selected": {
|
||||||
|
color: "#495057",
|
||||||
|
backgroundColor: "red",
|
||||||
|
borderColor: `#dee2e6 #dee2e6 #fff`,
|
||||||
|
},
|
||||||
|
"& .MuiTabs-indicator": {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,4 +1,4 @@
|
||||||
export interface User {
|
export interface I_User {
|
||||||
email: string;
|
email: string;
|
||||||
hasWriteAccess: boolean;
|
hasWriteAccess: boolean;
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -14,7 +14,7 @@ export interface User {
|
||||||
export interface UserWithInheritedAccess {
|
export interface UserWithInheritedAccess {
|
||||||
folderId: number;
|
folderId: number;
|
||||||
folderName: string;
|
folderName: string;
|
||||||
user: User;
|
user: I_User;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const filterDuplicateUsers = (data: any[]) => {
|
export const filterDuplicateUsers = (data: any[]) => {
|
||||||
|
|
Loading…
Reference in New Issue