-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding visualization and editing support for exercise admins and for …
…changing the author of the exercise.
- Loading branch information
1 parent
17b4623
commit 0d259d8
Showing
14 changed files
with
426 additions
and
60 deletions.
There are no files selected for viewing
89 changes: 89 additions & 0 deletions
89
src/components/Exercises/EditExerciseUsers/EditExerciseUsers.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { FormattedMessage } from 'react-intl'; | ||
import { Table } from 'react-bootstrap'; | ||
|
||
import ExerciseUserButtonsContainer from '../../../containers/ExerciseUserButtonsContainer'; | ||
import AddUserContainer from '../../../containers/AddUserContainer'; | ||
import UsersNameContainer from '../../../containers/UsersNameContainer'; | ||
import Box from '../../widgets/Box'; | ||
import Explanation from '../../widgets/Explanation'; | ||
import { AdminIcon, AuthorIcon } from '../../icons'; | ||
|
||
import { knownRoles, isSupervisorRole } from '../../helpers/usersRoles'; | ||
|
||
const ROLES_FILTER = knownRoles.filter(isSupervisorRole); | ||
|
||
const EditExerciseUsers = ({ exercise, instanceId }) => { | ||
return ( | ||
<Box | ||
type="warning" | ||
title={<FormattedMessage id="app.editExercise.manageUsers" defaultMessage="Manage related users" />} | ||
noPadding> | ||
<> | ||
<Table className="border-bottom mb-1"> | ||
<tbody> | ||
<tr> | ||
<td className="text-center text-muted shrink-col em-padding-left em-padding-right"> | ||
<AuthorIcon fixedWidth gapLeft /> | ||
</td> | ||
<th> | ||
<FormattedMessage id="generic.author" defaultMessage="Author" />: | ||
</th> | ||
<td> | ||
<UsersNameContainer userId={exercise.authorId} showEmail="icon" link /> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td className="text-center text-muted shrink-col em-padding-left em-padding-right"> | ||
<AdminIcon fixedWidth gapLeft /> | ||
</td> | ||
<th> | ||
<FormattedMessage id="app.exercise.admins" defaultMessage="Administrators" />: | ||
<Explanation id="admins"> | ||
<FormattedMessage | ||
id="app.exercise.admins.explanation" | ||
defaultMessage="The administrators have the same permissions as the author towards the exercise, but they are not explicitly mentioned in listings or used in search filters." | ||
/> | ||
</Explanation> | ||
</th> | ||
<td> | ||
{exercise.adminsIds.map(id => ( | ||
<div key={id} className="mb-2"> | ||
<UsersNameContainer userId={id} showEmail="icon" link /> | ||
<span className="float-right mr-2"> | ||
<ExerciseUserButtonsContainer userId={id} exercise={exercise} /> | ||
</span> | ||
</div> | ||
))} | ||
{exercise.adminsIds.length === 0 && ( | ||
<em className="small text-muted"> | ||
<FormattedMessage id="app.exercise.noAdmins" defaultMessage="no administrators appointed" /> | ||
</em> | ||
)} | ||
</td> | ||
</tr> | ||
</tbody> | ||
</Table> | ||
|
||
{(exercise.permissionHints.changeAuthor || exercise.permissionHints.updateAdmins) && ( | ||
<div className="m-3 mt-1"> | ||
<AddUserContainer | ||
instanceId={instanceId} | ||
id={`add-exercise-user-${exercise.id}`} | ||
rolesFilter={ROLES_FILTER} | ||
createActions={({ id }) => <ExerciseUserButtonsContainer userId={id} exercise={exercise} />} | ||
/> | ||
</div> | ||
)} | ||
</> | ||
</Box> | ||
); | ||
}; | ||
|
||
EditExerciseUsers.propTypes = { | ||
instanceId: PropTypes.string.isRequired, | ||
exercise: PropTypes.object.isRequired, | ||
}; | ||
|
||
export default EditExerciseUsers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import EditExerciseUsers from './EditExerciseUsers'; | ||
export default EditExerciseUsers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
128 changes: 128 additions & 0 deletions
128
src/containers/ExerciseUserButtonsContainer/ExerciseUserButtonsContainer.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { connect } from 'react-redux'; | ||
import { OverlayTrigger, Tooltip } from 'react-bootstrap'; | ||
import { FormattedMessage } from 'react-intl'; | ||
|
||
import { setAuthor, setAdmins } from '../../redux/modules/exercises'; | ||
import { getExerciseSetAuthorStatus, getExerciseSetAdminsStatus } from '../../redux/selectors/exercises'; | ||
|
||
import Button, { TheButtonGroup } from '../../components/widgets/TheButton'; | ||
import { AdminIcon, AuthorIcon, LoadingIcon, WarningIcon } from '../../components/icons'; | ||
|
||
const ExerciseUserButtonsContainer = ({ | ||
exercise, | ||
userId, | ||
setAuthor, | ||
setAuthorStatus, | ||
addAdmin, | ||
removeAdmin, | ||
setAdminsStatus, | ||
size = 'xs', | ||
}) => { | ||
const isAdmin = exercise.adminsIds && exercise.adminsIds.includes(userId); | ||
return exercise.authorId !== userId ? ( | ||
<TheButtonGroup> | ||
{exercise.permissionHints.changeAuthor && ( | ||
<OverlayTrigger | ||
placement="bottom" | ||
overlay={ | ||
<Tooltip id={`authorButton-${userId}`}> | ||
<FormattedMessage | ||
id="app.editExercise.setAuthorButton" | ||
defaultMessage="Make this user an author of the exercise (replacing current author)" | ||
/> | ||
</Tooltip> | ||
}> | ||
<Button | ||
variant={setAuthorStatus === false ? 'danger' : 'warning'} | ||
size={size} | ||
onClick={setAuthor} | ||
disabled={Boolean(setAuthorStatus)}> | ||
{setAuthorStatus === false && <WarningIcon fixedWidth smallGapRight />} | ||
{setAuthorStatus ? <LoadingIcon fixedWidth /> : <AuthorIcon fixedWidth />} | ||
</Button> | ||
</OverlayTrigger> | ||
)} | ||
|
||
{exercise.permissionHints.updateAdmins && ( | ||
<OverlayTrigger | ||
placement="bottom" | ||
overlay={ | ||
<Tooltip id={`adminButton-${userId}`}> | ||
{isAdmin ? ( | ||
<FormattedMessage | ||
id="app.editExercise.removeAdminButton" | ||
defaultMessage="Remove the user from exercise admins" | ||
/> | ||
) : ( | ||
<FormattedMessage | ||
id="app.editExercise.addAdminButton" | ||
defaultMessage="Make the user an exercise admin" | ||
/> | ||
)} | ||
</Tooltip> | ||
}> | ||
<Button | ||
variant={isAdmin || setAuthorStatus === false ? 'danger' : 'success'} | ||
size={size} | ||
onClick={isAdmin ? removeAdmin : addAdmin} | ||
disabled={Boolean(setAdminsStatus)}> | ||
{setAdminsStatus === false && <WarningIcon fixedWidth smallGapRight />} | ||
{setAdminsStatus ? ( | ||
<LoadingIcon fixedWidth /> | ||
) : isAdmin ? ( | ||
<AdminIcon fixedWidth /> | ||
) : ( | ||
<AdminIcon fixedWidth /> | ||
)} | ||
</Button> | ||
</OverlayTrigger> | ||
)} | ||
</TheButtonGroup> | ||
) : ( | ||
<OverlayTrigger | ||
placement="bottom" | ||
overlay={ | ||
<Tooltip id={`disabledButton-${userId}`}> | ||
<FormattedMessage | ||
id="app.editExercise.userIsAuthor" | ||
defaultMessage="The user is the author of the exercise" | ||
/> | ||
</Tooltip> | ||
}> | ||
<Button variant="secondary" size={size} disabled> | ||
<AuthorIcon fixedWidth /> | ||
</Button> | ||
</OverlayTrigger> | ||
); | ||
}; | ||
|
||
ExerciseUserButtonsContainer.propTypes = { | ||
userId: PropTypes.string.isRequired, | ||
exercise: PropTypes.object.isRequired, | ||
setAuthorStatus: PropTypes.bool, | ||
setAdminsStatus: PropTypes.bool, | ||
size: PropTypes.string, | ||
setAuthor: PropTypes.func.isRequired, | ||
addAdmin: PropTypes.func.isRequired, | ||
removeAdmin: PropTypes.func.isRequired, | ||
}; | ||
|
||
export default connect( | ||
(state, { exercise }) => ({ | ||
setAuthorStatus: getExerciseSetAuthorStatus(state, exercise.id), | ||
setAdminsStatus: getExerciseSetAdminsStatus(state, exercise.id), | ||
}), | ||
(dispatch, { exercise, userId }) => ({ | ||
setAuthor: () => dispatch(setAuthor(exercise.id, userId)), | ||
addAdmin: () => dispatch(setAdmins(exercise.id, [...exercise.adminsIds, userId])), | ||
removeAdmin: () => | ||
dispatch( | ||
setAdmins( | ||
exercise.id, | ||
exercise.adminsIds.filter(id => id !== userId) | ||
) | ||
), | ||
}) | ||
)(ExerciseUserButtonsContainer); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import ExerciseUserButtonsContainer from './ExerciseUserButtonsContainer'; | ||
export default ExerciseUserButtonsContainer; |
Oops, something went wrong.