[WIP] add dialog for choosing where to move folder or installation, work on access rights page

This commit is contained in:
Sina Blattmann 2023-03-24 13:06:33 +01:00
parent 05dc389eaf
commit 78b9d22fbd
10 changed files with 173 additions and 144 deletions

View File

@ -5,7 +5,6 @@ import { useParams } from "react-router-dom";
import axiosConfig from "../../config/axiosConfig"; import axiosConfig from "../../config/axiosConfig";
import { I_Installation } from "../../util/types"; import { I_Installation } from "../../util/types";
import FolderForm from "./FolderForm"; import FolderForm from "./FolderForm";
import LocationForm from "./LocationForm";
const Folder = () => { const Folder = () => {
const { id } = useParams(); const { id } = useParams();
@ -32,7 +31,6 @@ const Folder = () => {
<> <>
<Box sx={{ py: 3 }}> <Box sx={{ py: 3 }}>
<FolderForm values={values} id={id} /> <FolderForm values={values} id={id} />
<LocationForm parentId={values.parentId} />
</Box> </Box>
</> </>
); );

View File

@ -7,6 +7,7 @@ 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;
@ -78,7 +79,8 @@ 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 />}
<Button variant="outlined" type="submit" sx={{ height: 40, ml: 2 }}> <MoveDialog values={values} />
<Button variant="outlined" type="submit" sx={{ ml: 2 }}>
<FormattedMessage <FormattedMessage
id="applyChanges" id="applyChanges"
defaultMessage="Apply changes" defaultMessage="Apply changes"

View File

@ -37,12 +37,7 @@ const LocationForm = (props: LocationFormProps) => {
<FormattedMessage id="location" defaultMessage="Location" /> <FormattedMessage id="location" defaultMessage="Location" />
</InputLabel> </InputLabel>
</Grid> </Grid>
<Grid item xs={9} display="inline"> <Grid item xs={9} display="inline"></Grid>
<MoveTree
setSelectedParentId={setSelectedParentId}
parentId={selectedParentId}
/>
</Grid>
<Grid container justifyContent="flex-end" sx={{ pt: 1 }}> <Grid container justifyContent="flex-end" sx={{ pt: 1 }}>
<Button <Button
variant="outlined" variant="outlined"

View File

@ -0,0 +1,70 @@
import { Button, DialogActions, Dialog, DialogTitle } from "@mui/material";
import DialogContent from "@mui/material/DialogContent";
import { useContext, useState } from "react";
import { FormattedMessage } from "react-intl";
import { useParams } from "react-router-dom";
import axiosConfig from "../../../config/axiosConfig";
import { I_Folder } from "../../../util/types";
import { GroupContext } from "../../Context/GroupContextProvider";
import MoveTree from "./MoveTree";
interface MoveDialogProps {
values: I_Folder;
}
const MoveDialog = (props: MoveDialogProps) => {
const { values } = props;
const [open, setOpen] = useState(false);
const [selectedParentId, setSelectedParentId] = useState<number>(
values.parentId
);
const { fetchData, currentType } = useContext(GroupContext);
const { id } = useParams();
const handleMove = () => {
const route =
currentType === "Folder" ? "/MoveFolder" : "/MoveInstallation";
const paramsProp = currentType === "Folder" ? "folderId" : "installationId";
axiosConfig
.put(route, null, {
params: { [paramsProp]: id, parentId: selectedParentId },
})
.then((res) => {
setOpen(false);
fetchData();
});
};
return (
<div>
<Button variant="outlined" onClick={() => setOpen(true)}>
<FormattedMessage id="move" defaultMessage="Move" />
</Button>
<Dialog
onClose={() => setOpen(false)}
aria-labelledby="customized-dialog-title"
open={open}
sx={{
".MuiDialogContent-root": { overflowX: "hidden" },
maxHeight: 500,
}}
scroll="paper"
>
<DialogTitle>Move to</DialogTitle>
<DialogContent>
<MoveTree
parentId={selectedParentId}
setSelectedParentId={setSelectedParentId}
/>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleMove}>
Move
</Button>
</DialogActions>
</Dialog>
</div>
);
};
export default MoveDialog;

View File

@ -45,7 +45,6 @@ const MoveTree = (props: MoveTreeProps) => {
defaultCollapseIcon={<ExpandMoreIcon />} defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />} defaultExpandIcon={<ChevronRightIcon />}
sx={{ sx={{
height: 200,
flexGrow: 1, flexGrow: 1,
width: 400, width: 400,
overflow: "auto", overflow: "auto",

View File

@ -1,10 +1,4 @@
import { import { Autocomplete, Button, Grid, TextField } from "@mui/material";
Autocomplete,
Button,
CircularProgress,
Grid,
TextField,
} from "@mui/material";
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, 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";
@ -12,10 +6,6 @@ import axiosConfig from "../../../config/axiosConfig";
import { User } from "../../../util/user.util"; import { User } from "../../../util/user.util";
import { GroupContext } from "../../Context/GroupContextProvider"; import { GroupContext } from "../../Context/GroupContextProvider";
interface AutoCompleteOption {
label: string;
id: number;
}
const AvailableUserList = () => { const AvailableUserList = () => {
const [users, setUsers] = useState<User[]>([]); const [users, setUsers] = useState<User[]>([]);
const [selectedUsers, setSelectedUsers] = useState<User[]>([]); const [selectedUsers, setSelectedUsers] = useState<User[]>([]);

View File

@ -1,5 +1,6 @@
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
import AvailableUserList from "./AvailableUserList"; import AvailableUserList from "./AvailableUserList";
import InnovenergyList from "./UserList";
import UsersWithDirectAccess from "./UsersWithDirectAccess"; import UsersWithDirectAccess from "./UsersWithDirectAccess";
import UsersWithInheritedAccess from "./UsersWithInheritedAccess"; import UsersWithInheritedAccess from "./UsersWithInheritedAccess";
@ -8,8 +9,12 @@ const Users = () => {
<> <>
<AvailableUserList /> <AvailableUserList />
<Grid container sx={{ mt: 1 }}> <Grid container sx={{ mt: 1 }}>
<UsersWithInheritedAccess /> <Grid item xs={6}>
<InnovenergyList>
<UsersWithDirectAccess /> <UsersWithDirectAccess />
<UsersWithInheritedAccess />
</InnovenergyList>
</Grid>
</Grid> </Grid>
</> </>
); );

View File

@ -1,12 +1,10 @@
import { import {
Grid,
ListItem, ListItem,
ListItemText, ListItemText,
Divider, Divider,
IconButton, IconButton,
Avatar, Avatar,
ListItemAvatar, ListItemAvatar,
Typography,
} from "@mui/material"; } from "@mui/material";
import { Fragment, useCallback, useContext, useEffect, useState } from "react"; import { Fragment, useCallback, useContext, useEffect, useState } from "react";
import axiosConfig from "../../../config/axiosConfig"; import axiosConfig from "../../../config/axiosConfig";
@ -14,9 +12,7 @@ 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 { GroupContext } from "../../Context/GroupContextProvider";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import InnovenergyList from "./UserList";
import PersonIcon from "@mui/icons-material/Person"; import PersonIcon from "@mui/icons-material/Person";
import { FormattedMessage } from "react-intl";
const UsersWithDirectAccess = () => { const UsersWithDirectAccess = () => {
const [users, setUsers] = useState<User[]>(); const [users, setUsers] = useState<User[]>();
@ -26,12 +22,7 @@ const UsersWithDirectAccess = () => {
const fetchUsers = useCallback(async () => { const fetchUsers = useCallback(async () => {
axiosConfig axiosConfig
.get( .get("/GetUsersWithDirectAccessTo" + currentType, { params: { id } })
currentType === "Folder"
? "/GetUsersWithDirectAccessToFolder"
: "/GetUsersWithDirectAccessToInstallation",
{ params: { id } }
)
.then((res) => { .then((res) => {
setUsers(res.data); setUsers(res.data);
}); });
@ -39,17 +30,15 @@ const UsersWithDirectAccess = () => {
useEffect(() => { useEffect(() => {
fetchUsers(); fetchUsers();
}, [currentType, fetchUsers, id]); }, [fetchUsers]);
const handleIconClick = (userId: number) => { const handleIconClick = (userId: number) => {
const folderId = id ? parseInt(id) : ""; const folderId = id ? parseInt(id) : "";
axiosConfig axiosConfig
.post( .post("/RevokeUserAccessTo" + currentType, {
currentType === "Folder" userId: userId,
? "/RevokeUserAccessToFolder" folderId: folderId,
: "/RevokeUserAccessToInstallation", })
{ userId: userId, folderId: folderId }
)
.then((res) => { .then((res) => {
fetchUsers(); fetchUsers();
}); });
@ -57,14 +46,7 @@ const UsersWithDirectAccess = () => {
if (users && users.length) { if (users && users.length) {
return ( return (
<Grid item xs={6}> <>
<Typography variant="overline" gutterBottom>
<FormattedMessage
id="usersWithDirectAccess"
defaultMessage="Users with direct access"
/>
</Typography>
<InnovenergyList>
{users.map((user) => { {users.map((user) => {
return ( return (
<Fragment key={user.id}> <Fragment key={user.id}>
@ -89,8 +71,7 @@ const UsersWithDirectAccess = () => {
</Fragment> </Fragment>
); );
})} })}
</InnovenergyList> </>
</Grid>
); );
} }
return null; return null;

View File

@ -1,21 +1,20 @@
import { import {
Grid,
ListItem, ListItem,
ListItemText, ListItemText,
Divider, Divider,
ListItemAvatar, ListItemAvatar,
Avatar, Avatar,
Typography,
} from "@mui/material"; } from "@mui/material";
import { Fragment, useContext, useEffect, useState } from "react"; import { Fragment, useCallback, useContext, useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import axiosConfig from "../../../config/axiosConfig"; import axiosConfig from "../../../config/axiosConfig";
import { UserWithInheritedAccess } from "../../../util/user.util"; import {
filterDuplicateUsers,
UserWithInheritedAccess,
} from "../../../util/user.util";
import { GroupContext } from "../../Context/GroupContextProvider"; import { GroupContext } from "../../Context/GroupContextProvider";
import routes from "../../../routes.json"; import routes from "../../../routes.json";
import InnovenergyList from "./UserList";
import PersonIcon from "@mui/icons-material/Person"; import PersonIcon from "@mui/icons-material/Person";
import { FormattedMessage } from "react-intl";
const UsersWithInheritedAccess = () => { const UsersWithInheritedAccess = () => {
const [users, setUsers] = useState<UserWithInheritedAccess[]>(); const [users, setUsers] = useState<UserWithInheritedAccess[]>();
@ -23,47 +22,21 @@ const UsersWithInheritedAccess = () => {
const { currentType } = useContext(GroupContext); const { currentType } = useContext(GroupContext);
const { id } = useParams(); const { id } = useParams();
useEffect(() => { const fetchUsers = useCallback(async () => {
axiosConfig axiosConfig
.get( .get("/GetUsersWithInheritedAccessTo" + currentType, { params: { id } })
currentType === "Folder"
? "/GetUsersWithInheritedAccessToFolder"
: "/GetUsersWithInheritedAccessToInstallation",
{ params: { id } }
)
.then((res) => { .then((res) => {
setUsers(res.data); setUsers(res.data);
}); });
}, [currentType, id]); }, [currentType, id]);
const filterDuplicateUsers = (data: any[]) => { useEffect(() => {
return data.reduce( fetchUsers();
(prev: UserWithInheritedAccess[], curr: UserWithInheritedAccess) => { }, [fetchUsers]);
const foundUser = prev.find(({ user }) => user.id === curr.user.id);
if (foundUser) {
const prevIds = foundUser.user.folderIds
? foundUser.user.folderIds
: [];
foundUser.user.folderIds = [...prevIds, curr.user.parentId];
return prev;
}
curr.user.folderIds = [curr.user.parentId];
prev.push(curr);
return prev;
},
[]
);
};
if (users && users.length) { if (users && users.length) {
return ( return (
<Grid item xs={6}> <>
<Typography variant="overline" gutterBottom>
<FormattedMessage
id="usersWithInheritedAccess"
defaultMessage="Users with inherited access"
/>
</Typography>
<InnovenergyList>
{filterDuplicateUsers(users).map( {filterDuplicateUsers(users).map(
({ user, folderName }: UserWithInheritedAccess) => { ({ user, folderName }: UserWithInheritedAccess) => {
return ( return (
@ -79,9 +52,7 @@ const UsersWithInheritedAccess = () => {
secondary={ secondary={
<> <>
Inherited access from{" "} Inherited access from{" "}
<Link <Link to={routes.groups + routes.users + user.parentId}>
to={routes.groups + routes.users + user.parentId}
>
{folderName} {folderName}
</Link> </Link>
</> </>
@ -93,8 +64,7 @@ const UsersWithInheritedAccess = () => {
); );
} }
)} )}
</InnovenergyList> </>
</Grid>
); );
} }
return null; return null;

View File

@ -16,3 +16,22 @@ export interface UserWithInheritedAccess {
folderName: string; folderName: string;
user: User; user: User;
} }
export const filterDuplicateUsers = (data: any[]) => {
return data.reduce(
(prev: UserWithInheritedAccess[], curr: UserWithInheritedAccess) => {
const foundUser = prev.find(({ user }) => user.id === curr.user.id);
if (foundUser) {
const prevIds = foundUser.user.folderIds
? foundUser.user.folderIds
: [];
foundUser.user.folderIds = [...prevIds, curr.user.parentId];
return prev;
}
curr.user.folderIds = [curr.user.parentId];
prev.push(curr);
return prev;
},
[]
);
};