2021-10-12 13:21:45 +02:00
|
|
|
/* eslint-disable react/jsx-no-target-blank */
|
2021-05-18 12:59:48 +02:00
|
|
|
import { useEffect, useState } from 'react';
|
2021-03-11 13:59:20 +01:00
|
|
|
import {
|
2021-04-07 09:04:48 +02:00
|
|
|
Avatar,
|
|
|
|
Button,
|
|
|
|
Dialog,
|
|
|
|
DialogActions,
|
|
|
|
DialogContent,
|
|
|
|
DialogContentText,
|
|
|
|
DialogTitle,
|
2021-03-11 13:59:20 +01:00
|
|
|
List,
|
|
|
|
ListItem,
|
2021-04-07 09:04:48 +02:00
|
|
|
ListItemAvatar,
|
2021-03-11 13:59:20 +01:00
|
|
|
ListItemSecondaryAction,
|
|
|
|
ListItemText,
|
|
|
|
MenuItem,
|
|
|
|
} from '@material-ui/core';
|
2021-06-29 10:21:54 +02:00
|
|
|
import { Delete } from '@material-ui/icons';
|
2021-10-12 13:21:45 +02:00
|
|
|
import { Alert } from '@material-ui/lab';
|
2021-03-11 13:59:20 +01:00
|
|
|
|
2022-01-17 11:41:44 +01:00
|
|
|
import AddUserComponent from '../access-add-user';
|
2021-03-11 13:59:20 +01:00
|
|
|
|
2022-01-17 11:41:44 +01:00
|
|
|
import projectApi from '../../../store/project/api';
|
|
|
|
import PageContent from '../../common/PageContent';
|
|
|
|
import useUiConfig from '../../../hooks/api/getters/useUiConfig/useUiConfig';
|
|
|
|
import { useStyles } from './ProjectAccess.styles';
|
|
|
|
import PermissionIconButton from '../../common/PermissionIconButton/PermissionIconButton';
|
|
|
|
import { useParams } from 'react-router-dom';
|
|
|
|
import { IFeatureViewParams } from '../../../interfaces/params';
|
|
|
|
import ProjectRoleSelect from './ProjectRoleSelect/ProjectRoleSelect';
|
2022-01-20 10:12:27 +01:00
|
|
|
import usePagination from '../../../hooks/usePagination';
|
|
|
|
import PaginateUI from '../../common/PaginateUI/PaginateUI';
|
2022-01-20 13:07:54 +01:00
|
|
|
import useToast from '../../../hooks/useToast';
|
|
|
|
import ConfirmDialogue from '../../common/Dialogue';
|
2021-10-12 13:21:45 +02:00
|
|
|
|
2022-01-17 11:41:44 +01:00
|
|
|
const ProjectAccess = () => {
|
|
|
|
const { id } = useParams<IFeatureViewParams>();
|
|
|
|
const styles = useStyles();
|
2021-03-11 13:59:20 +01:00
|
|
|
const [roles, setRoles] = useState([]);
|
|
|
|
const [users, setUsers] = useState([]);
|
|
|
|
const [error, setError] = useState();
|
2022-01-20 13:07:54 +01:00
|
|
|
const { setToastData, setToastApiError } = useToast();
|
2021-10-12 13:21:45 +02:00
|
|
|
const { isOss } = useUiConfig();
|
2022-01-20 10:12:27 +01:00
|
|
|
const { page, pages, nextPage, prevPage, setPageIndex, pageIndex } =
|
|
|
|
usePagination(users, 10);
|
2022-01-20 13:07:54 +01:00
|
|
|
const [showDelDialogue, setShowDelDialogue] = useState(false);
|
|
|
|
const [user, setUser] = useState({});
|
2021-10-12 13:21:45 +02:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
fetchAccess();
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2022-01-17 11:41:44 +01:00
|
|
|
}, [id]);
|
2021-03-11 13:59:20 +01:00
|
|
|
|
|
|
|
const fetchAccess = async () => {
|
2021-07-07 11:04:36 +02:00
|
|
|
try {
|
2022-01-17 11:41:44 +01:00
|
|
|
const access = await projectApi.fetchAccess(id);
|
2021-07-07 11:04:36 +02:00
|
|
|
setRoles(access.roles);
|
|
|
|
setUsers(
|
|
|
|
access.users.map(u => ({ ...u, name: u.name || '(No name)' }))
|
|
|
|
);
|
|
|
|
} catch (e) {
|
2022-01-20 13:07:54 +01:00
|
|
|
setToastApiError(e.toString());
|
2021-07-07 11:04:36 +02:00
|
|
|
}
|
2021-03-11 13:59:20 +01:00
|
|
|
};
|
|
|
|
|
2021-10-12 13:21:45 +02:00
|
|
|
if (isOss()) {
|
|
|
|
return (
|
2022-01-17 11:41:44 +01:00
|
|
|
<PageContent>
|
|
|
|
<Alert severity="error">
|
|
|
|
Controlling access to projects requires a paid version of
|
|
|
|
Unleash. Check out{' '}
|
|
|
|
<a href="https://www.getunleash.io" target="_blank">
|
|
|
|
getunleash.io
|
|
|
|
</a>{' '}
|
|
|
|
to find out more.
|
|
|
|
</Alert>
|
|
|
|
</PageContent>
|
|
|
|
);
|
2021-03-11 13:59:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const handleRoleChange = (userId, currRoleId) => async evt => {
|
|
|
|
const roleId = evt.target.value;
|
|
|
|
try {
|
2022-01-17 11:41:44 +01:00
|
|
|
await projectApi.removeUserFromRole(id, currRoleId, userId);
|
2022-01-20 13:07:54 +01:00
|
|
|
await projectApi.addUserToRole(id, roleId, userId).then(() => {
|
|
|
|
setToastData({
|
|
|
|
type: 'success',
|
|
|
|
title: 'User role changed successfully',
|
|
|
|
});
|
|
|
|
});
|
2021-03-11 13:59:20 +01:00
|
|
|
const newUsers = users.map(u => {
|
|
|
|
if (u.id === userId) {
|
|
|
|
return { ...u, roleId };
|
|
|
|
} else return u;
|
|
|
|
});
|
|
|
|
setUsers(newUsers);
|
|
|
|
} catch (err) {
|
2022-01-20 13:07:54 +01:00
|
|
|
setToastData({
|
|
|
|
type: 'error',
|
|
|
|
title: err.message || 'Server problems when adding users.',
|
|
|
|
});
|
2021-03-11 13:59:20 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const addUser = async (userId, roleId) => {
|
|
|
|
try {
|
2022-01-17 11:41:44 +01:00
|
|
|
await projectApi.addUserToRole(id, roleId, userId);
|
2022-01-20 13:07:54 +01:00
|
|
|
await fetchAccess().then(() => {
|
|
|
|
setToastData({
|
|
|
|
type: 'success',
|
|
|
|
title: 'Successfully added user to the project',
|
|
|
|
});
|
|
|
|
});
|
2021-03-11 13:59:20 +01:00
|
|
|
} catch (err) {
|
2022-01-20 13:07:54 +01:00
|
|
|
setToastData({
|
|
|
|
type: 'error',
|
|
|
|
title: err.message || 'Server problems when adding users.',
|
|
|
|
});
|
2021-03-11 13:59:20 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-01-20 13:07:54 +01:00
|
|
|
const removeAccess = (userId: number, roleId: number) => async () => {
|
2021-03-11 13:59:20 +01:00
|
|
|
try {
|
2022-01-20 13:07:54 +01:00
|
|
|
await projectApi.removeUserFromRole(id, roleId, userId).then(() => {
|
|
|
|
setToastData({
|
|
|
|
type: 'success',
|
|
|
|
title: 'User have been removed from project',
|
|
|
|
});
|
|
|
|
});
|
2021-03-11 13:59:20 +01:00
|
|
|
const newUsers = users.filter(u => u.id !== userId);
|
|
|
|
setUsers(newUsers);
|
|
|
|
} catch (err) {
|
2022-01-20 13:07:54 +01:00
|
|
|
setToastData({
|
|
|
|
type: 'error',
|
|
|
|
title: err.message || 'Server problems when adding users.',
|
|
|
|
});
|
2021-03-11 13:59:20 +01:00
|
|
|
}
|
2022-01-20 13:07:54 +01:00
|
|
|
setShowDelDialogue(false);
|
2021-03-11 13:59:20 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleCloseError = () => {
|
|
|
|
setError(undefined);
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
2022-01-17 11:41:44 +01:00
|
|
|
<PageContent className={styles.pageContent}>
|
2021-03-11 13:59:20 +01:00
|
|
|
<AddUserComponent roles={roles} addUserToRole={addUser} />
|
|
|
|
<Dialog
|
|
|
|
open={!!error}
|
|
|
|
onClose={handleCloseError}
|
|
|
|
aria-labelledby="alert-dialog-title"
|
|
|
|
aria-describedby="alert-dialog-description"
|
|
|
|
>
|
|
|
|
<DialogTitle id="alert-dialog-title">{'Error'}</DialogTitle>
|
|
|
|
<DialogContent>
|
2021-05-18 12:59:48 +02:00
|
|
|
<DialogContentText id="alert-dialog-description">
|
|
|
|
{error}
|
|
|
|
</DialogContentText>
|
2021-03-11 13:59:20 +01:00
|
|
|
</DialogContent>
|
|
|
|
<DialogActions>
|
2021-05-18 12:59:48 +02:00
|
|
|
<Button
|
|
|
|
onClick={handleCloseError}
|
|
|
|
color="secondary"
|
|
|
|
autoFocus
|
|
|
|
>
|
2021-03-11 13:59:20 +01:00
|
|
|
Close
|
|
|
|
</Button>
|
|
|
|
</DialogActions>
|
|
|
|
</Dialog>
|
2022-01-17 11:41:44 +01:00
|
|
|
<div className={styles.divider}></div>
|
2021-03-11 13:59:20 +01:00
|
|
|
<List>
|
2022-01-20 10:12:27 +01:00
|
|
|
{page.map(user => {
|
2021-03-11 13:59:20 +01:00
|
|
|
const labelId = `checkbox-list-secondary-label-${user.id}`;
|
|
|
|
return (
|
|
|
|
<ListItem key={user.id} button>
|
|
|
|
<ListItemAvatar>
|
|
|
|
<Avatar alt={user.name} src={user.imageUrl} />
|
|
|
|
</ListItemAvatar>
|
2021-05-18 12:59:48 +02:00
|
|
|
<ListItemText
|
|
|
|
id={labelId}
|
|
|
|
primary={user.name}
|
|
|
|
secondary={user.email || user.username}
|
|
|
|
/>
|
|
|
|
<ListItemSecondaryAction
|
2022-01-17 11:41:44 +01:00
|
|
|
className={styles.actionList}
|
2021-05-18 12:59:48 +02:00
|
|
|
>
|
2022-01-17 11:41:44 +01:00
|
|
|
<ProjectRoleSelect
|
|
|
|
labelId={`role-${user.id}-select-label`}
|
|
|
|
id={`role-${user.id}-select`}
|
|
|
|
key={user.id}
|
|
|
|
placeholder="Choose role"
|
|
|
|
onChange={handleRoleChange(
|
|
|
|
user.id,
|
|
|
|
user.roleId
|
|
|
|
)}
|
|
|
|
roles={roles}
|
|
|
|
value={user.roleId || ''}
|
|
|
|
>
|
|
|
|
<MenuItem value="" disabled>
|
|
|
|
Choose role
|
|
|
|
</MenuItem>
|
|
|
|
</ProjectRoleSelect>
|
|
|
|
|
|
|
|
<PermissionIconButton
|
|
|
|
className={styles.iconButton}
|
2021-03-11 13:59:20 +01:00
|
|
|
edge="end"
|
|
|
|
aria-label="delete"
|
|
|
|
title="Remove access"
|
2022-01-20 13:07:54 +01:00
|
|
|
onClick={() => {
|
|
|
|
setUser(user);
|
|
|
|
setShowDelDialogue(true);
|
|
|
|
}}
|
2022-01-17 11:41:44 +01:00
|
|
|
disabled={users.length === 1}
|
|
|
|
tooltip={
|
|
|
|
users.length === 1
|
|
|
|
? 'A project must have at least one owner'
|
2022-01-20 10:12:27 +01:00
|
|
|
: 'Remove access'
|
2022-01-17 11:41:44 +01:00
|
|
|
}
|
2021-03-11 13:59:20 +01:00
|
|
|
>
|
2021-06-29 10:21:54 +02:00
|
|
|
<Delete />
|
2022-01-17 11:41:44 +01:00
|
|
|
</PermissionIconButton>
|
2021-03-11 13:59:20 +01:00
|
|
|
</ListItemSecondaryAction>
|
|
|
|
</ListItem>
|
|
|
|
);
|
|
|
|
})}
|
2022-01-20 10:12:27 +01:00
|
|
|
<PaginateUI
|
|
|
|
pages={pages}
|
|
|
|
pageIndex={pageIndex}
|
|
|
|
setPageIndex={setPageIndex}
|
|
|
|
nextPage={nextPage}
|
|
|
|
prevPage={prevPage}
|
|
|
|
style={{ bottom: '-21px' }}
|
|
|
|
/>
|
2021-03-11 13:59:20 +01:00
|
|
|
</List>
|
2022-01-20 13:07:54 +01:00
|
|
|
<ConfirmDialogue
|
|
|
|
open={showDelDialogue}
|
|
|
|
onClick={removeAccess(user.id, user.roleId)}
|
|
|
|
onClose={() => {
|
|
|
|
setUser({});
|
|
|
|
setShowDelDialogue(false);
|
|
|
|
}}
|
|
|
|
title="Really remove user from this project"
|
|
|
|
/>
|
2021-05-18 12:59:48 +02:00
|
|
|
</PageContent>
|
2021-03-11 13:59:20 +01:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-01-17 11:41:44 +01:00
|
|
|
export default ProjectAccess;
|