diff --git a/packages/components-faraday/src/components/Admin/AddEditUser.js b/packages/components-faraday/src/components/Admin/AddEditUser.js index f06c8c9da8e053647bbcd73581b6c0269561738d..b0ba223d6781fb31845e13bc89e476b8c911909e 100644 --- a/packages/components-faraday/src/components/Admin/AddEditUser.js +++ b/packages/components-faraday/src/components/Admin/AddEditUser.js @@ -1,10 +1,10 @@ import React from 'react' -import { get, map } from 'lodash' +import { get } from 'lodash' import { connect } from 'react-redux' import { reduxForm, SubmissionError } from 'redux-form' import styled from 'styled-components' import { actions } from 'pubsweet-client' -import { create } from 'pubsweet-client/src/helpers/api' +import { create, update } from 'pubsweet-client/src/helpers/api' import { withJournal } from 'xpub-journal' import { ConnectPage } from 'xpub-connect' import { selectUser } from 'xpub-selectors' @@ -13,35 +13,53 @@ import { compose, withProps, withHandlers, withState } from 'recompose' import AddUserForm from './AddUserForm' import EditUserForm from './EditUserForm' - -const getRoleOptions = journal => - map(journal.roles, (value, key) => ({ label: value, value: key })) +import { getRoleOptions, setAdmin, parseUpdateUser } from './utils' const onSubmit = (values, dispatch, { isEdit, history }) => { if (!isEdit) { - return create('/users/invite', values) + const newValues = setAdmin(values) + return create('/users/invite', newValues) .then(r => history.push('/admin/users')) .catch(error => { const err = get(error, 'response') if (err) { const errorMessage = get(JSON.parse(err), 'error') throw new SubmissionError({ - role: errorMessage || 'Something went wrong', + email: errorMessage || 'Something went wrong', }) } }) } + + return update(`/users/${values.id}`, parseUpdateUser(values)) + .then(() => { + history.push('/admin/users') + }) + .catch(error => { + const err = get(error, 'response') + if (err) { + const errorMessage = get(JSON.parse(err), 'error') + throw new SubmissionError({ + roles: errorMessage || 'Something went wrong', + }) + } + }) } -const AddEditUser = ({ handleSubmit, journal, isEdit, user }) => ( +const AddEditUser = ({ handleSubmit, journal, isEdit, user, history }) => ( <Root> <FormContainer onSubmit={handleSubmit}> {isEdit ? ( - <EditUserForm roles={getRoleOptions(journal)} user={user} /> + <EditUserForm + journal={journal} + roles={getRoleOptions(journal)} + user={user} + /> ) : ( - <AddUserForm roles={getRoleOptions(journal)} /> + <AddUserForm journal={journal} roles={getRoleOptions(journal)} /> )} <Row> + <Button onClick={history.goBack}>Back</Button> <Button primary type="submit"> Save user </Button> diff --git a/packages/components-faraday/src/components/Admin/AddUserForm.js b/packages/components-faraday/src/components/Admin/AddUserForm.js index fc043cc51edfadaffa24cd0f5b84e3b9e63bb2ff..3edc3f9e500465e37401e0da299398894686b55b 100644 --- a/packages/components-faraday/src/components/Admin/AddUserForm.js +++ b/packages/components-faraday/src/components/Admin/AddUserForm.js @@ -4,24 +4,24 @@ import { ValidatedField, TextField, Menu } from '@pubsweet/ui' import { required } from 'xpub-validators' -const AddUserForm = ({ roles }) => { - const roleOptions = roles.filter(r => r.value === 'editorInChief') +const AddUserForm = ({ roles, journal }) => { + const roleOptions = roles.filter(r => + ['editorInChief', 'author', 'admin'].includes(r.value), + ) return ( <div> <h3>Add user</h3> <Row> <RowItem> - <Label> Email</Label> + <Label> Email*</Label> <ValidatedField component={TextField} name="email" validate={[required]} /> </RowItem> - </Row> - <Row> <RowItem> - <Label> Role</Label> + <Label> Role*</Label> <ValidatedField component={input => <Menu {...input} options={roleOptions} />} name="role" @@ -29,6 +29,29 @@ const AddUserForm = ({ roles }) => { /> </RowItem> </Row> + <Row> + <RowItem> + <Label> First name </Label> + <ValidatedField component={TextField} name="firstName" /> + </RowItem> + <RowItem> + <Label> Last name </Label> + <ValidatedField component={TextField} name="lastName" /> + </RowItem> + </Row> + <Row> + <RowItem> + <Label> Affiliation </Label> + <ValidatedField component={TextField} name="affiliation" /> + </RowItem> + <RowItem> + <Label> Title </Label> + <ValidatedField + component={input => <Menu {...input} options={journal.title} />} + name="title" + /> + </RowItem> + </Row> </div> ) } diff --git a/packages/components-faraday/src/components/Admin/AdminUsers.js b/packages/components-faraday/src/components/Admin/AdminUsers.js index 889a82f506d445bf956caebb4cb8e4d6230a39cf..867e9991e76749bb822f3b2bec91a86400151f5b 100644 --- a/packages/components-faraday/src/components/Admin/AdminUsers.js +++ b/packages/components-faraday/src/components/Admin/AdminUsers.js @@ -5,24 +5,47 @@ import styled from 'styled-components' import { Icon, Menu } from '@pubsweet/ui' import { actions } from 'pubsweet-client' import { ConnectPage } from 'xpub-connect' +import { withJournal } from 'xpub-journal' import { withRouter } from 'react-router-dom' import { compose, withState, withHandlers } from 'recompose' import { Pagination } from './' -const TableRow = ({ toggleUser, selected, email, username, type }) => ( +const TableRow = ({ + toggleUser, + selected, + id, + email, + roles, + username, + title = '', + firstName = '', + lastName = '', + affiliation, + isConfirmed, + roleOptions, +}) => ( <Row> <td> <Input checked={selected} onClick={toggleUser} type="checkbox" /> </td> <td>{email}</td> - <td>{username}</td> - <td>affiliation here</td> - <td>country here</td> - <td>{type}</td> - <Status> - <span>status</span> - </Status> + <td>{`${firstName} ${lastName}`}</td> + <td>{affiliation}</td> + <td> + {roles && + roles.map((r, i, arr) => ( + <Role key={r}>{`${roleOptions[r]}${ + i !== arr.length - 1 ? ', ' : '' + }`}</Role> + ))} + </td> + <td> + <Tag>{isConfirmed ? 'Confirmed' : 'Invited'}</Tag> + </td> + <td> + <Action href={`/admin/users/edit/${id}`}>Edit</Action> + </td> </Row> ) @@ -35,13 +58,17 @@ const Users = ({ page, itemsPerPage, history, + journal, }) => ( <div> <Header> - <span>Users</span> + <BreadCrumbs> + <span>Admin Dashboard</span> + <span>Users</span> + </BreadCrumbs> <AddButton onClick={() => history.push('/admin/users/add')}> <Icon color="#667080">plus-circle</Icon> - Add User + Add User </AddButton> </Header> <SubHeader> @@ -90,14 +117,19 @@ const Users = ({ <td>Email</td> <td>Full name</td> <td>Affiliation</td> - <td>Country</td> - <td>Roles</td> + <td width="200">Roles</td> <td>Status</td> + <td width="50" /> </tr> </thead> <tbody> {users.map(u => ( - <TableRow key={u.id} {...u} toggleUser={toggleUser(u)} /> + <TableRow + key={u.id} + {...u} + roleOptions={journal.roles} + toggleUser={toggleUser(u)} + /> ))} </tbody> </Table> @@ -107,6 +139,7 @@ const Users = ({ export default compose( ConnectPage(() => [actions.getUsers()]), withRouter, + withJournal, connect(state => ({ currentUsers: get(state, 'users.users') })), withState('users', 'setUsers', props => props.currentUsers.map(u => ({ ...u, selected: false })), @@ -152,13 +185,24 @@ const Header = styled.div` display: flex; flex-direction: row; align-items: center; +` +const BreadCrumbs = styled.div` & span { - font-family: Helvetica; - font-size: 24px; - font-weight: bold; + font-size: 17px; text-align: left; color: #667080; + + &:after { + content: '>'; + padding: 0 10px; + } + &:last-child { + font-size: 24px; + font-weight: bold; + &:after { + content: ''; + } } ` @@ -188,7 +232,7 @@ const Table = styled.table` border-spacing: 0; border-collapse: collapse; margin-top: 10px; - width: 100vw; + width: 100%; & thead tr { height: 40px; @@ -208,19 +252,37 @@ const Row = styled.tr` font-size: 14px; height: 40px; text-align: left; + &:hover { + background-color: #e6e7e9; + a { + display: block; + } + } ` -const Status = styled.td` - & span { - border: solid 1px #667080; - text-transform: uppercase; - font-family: Helvetica; - font-size: 12px; - font-weight: bold; - text-align: left; - color: #667080; - padding: 2px 10px; - } +const Tag = styled.span` + border: solid 1px #667080; + text-transform: uppercase; + font-family: Helvetica; + font-size: 12px; + font-weight: bold; + text-align: left; + color: #667080; + padding: 2px 10px; + margin-right: 5px; +` + +const Role = styled.span` + height: 17px; + font-size: 14px; + text-align: left; + color: #667080; + text-transform: uppercase; +` + +const Action = styled.a` + color: #667080; + display: none; ` const Input = styled.input` diff --git a/packages/components-faraday/src/components/Admin/EditUserForm.js b/packages/components-faraday/src/components/Admin/EditUserForm.js index 9020fbf3d987b96d29883209222e0a2e468d9b59..d6608c363c3838ed17d4732051bed3bd2de627f6 100644 --- a/packages/components-faraday/src/components/Admin/EditUserForm.js +++ b/packages/components-faraday/src/components/Admin/EditUserForm.js @@ -1,12 +1,12 @@ import React from 'react' import styled from 'styled-components' -import { ValidatedField, TextField, Menu } from '@pubsweet/ui' +import { ValidatedField, TextField, Menu, CheckboxGroup } from '@pubsweet/ui' import { required } from 'xpub-validators' -const EditUserForm = ({ roles, user }) => { +const EditUserForm = ({ roles, journal, user }) => { const roleOptions = roles.filter(r => - ['editorInChief', 'author'].includes(r.value), + ['editorInChief', 'author', 'admin'].includes(r.value), ) return ( <div> @@ -14,7 +14,7 @@ const EditUserForm = ({ roles, user }) => { <h5>{user.email}</h5> <Row> <RowItem> - <Label> First name </Label> + <Label> First name* </Label> <ValidatedField component={TextField} name="firstName" @@ -22,11 +22,7 @@ const EditUserForm = ({ roles, user }) => { /> </RowItem> <RowItem> - <Label> Middle name </Label> - <ValidatedField component={TextField} name="middleName" /> - </RowItem> - <RowItem> - <Label> Last name </Label> + <Label> Last name* </Label> <ValidatedField component={TextField} name="lastName" @@ -36,7 +32,7 @@ const EditUserForm = ({ roles, user }) => { </Row> <Row> <RowItem> - <Label> Affiliation </Label> + <Label> Affiliation* </Label> <ValidatedField component={TextField} name="affiliation" @@ -44,14 +40,21 @@ const EditUserForm = ({ roles, user }) => { /> </RowItem> <RowItem> - <Label> Title </Label> - <ValidatedField component={TextField} name="title" /> + <Label> Title* </Label> + <ValidatedField + component={input => <Menu {...input} options={journal.title} />} + name="title" + /> </RowItem> + </Row> + <Row> <RowItem> - <Label> Role</Label> + <Label> Roles*</Label> <ValidatedField - component={input => <Menu {...input} options={roleOptions} />} - name="role" + component={input => ( + <CheckboxGroup {...input} options={roleOptions} /> + )} + name="roles" /> </RowItem> </Row> diff --git a/packages/components-faraday/src/components/Admin/utils.js b/packages/components-faraday/src/components/Admin/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..de7049e4123d0e4ef6ef174a757da7024a4fae50 --- /dev/null +++ b/packages/components-faraday/src/components/Admin/utils.js @@ -0,0 +1,30 @@ +import { pick, map } from 'lodash' + +export const getRoleOptions = journal => + map(journal.roles, (value, key) => ({ label: value, value: key })) + +export const setAdmin = values => { + const newValues = { ...values } + if (newValues.roles && newValues.roles.includes('admin')) { + newValues.admin = true + } else { + newValues.admin = false + } + + return newValues +} + +export const parseUpdateUser = values => { + const valuesToSave = [ + 'admin', + 'firstName', + 'lastName', + 'affiliation', + 'title', + 'roles', + 'rev', + ] + + const newValues = setAdmin(values) + return pick(newValues, valuesToSave) +} diff --git a/packages/components-faraday/src/components/SignUp/SignUpInvitationForm.js b/packages/components-faraday/src/components/SignUp/SignUpInvitationForm.js index 9f4a61f1437d83927db7145d3c8d103714eddc7c..f37aa43b340aaba2d87e8f46bd04adf4332061c5 100644 --- a/packages/components-faraday/src/components/SignUp/SignUpInvitationForm.js +++ b/packages/components-faraday/src/components/SignUp/SignUpInvitationForm.js @@ -11,6 +11,7 @@ const SignUpInvitation = ({ step, nextStep, submitConfirmation, + initialValues, }) => ( <Root> <Title>Add New Account Details</Title> @@ -19,8 +20,20 @@ const SignUpInvitation = ({ your password. </Subtitle> <Email>{email}</Email> - {step === 0 && <Step0 journal={journal} onSubmit={nextStep} />} - {step === 1 && <Step1 journal={journal} onSubmit={submitConfirmation} />} + {step === 0 && ( + <Step0 + initialValues={initialValues} + journal={journal} + onSubmit={nextStep} + /> + )} + {step === 1 && ( + <Step1 + initialValues={initialValues} + journal={journal} + onSubmit={submitConfirmation} + /> + )} </Root> ) diff --git a/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js b/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js index 314d2899c4d60ff5ddadae6f58097339c42ab61c..31be2fafeb6bb2be8eecac0196076f19705a8c44 100644 --- a/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js +++ b/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js @@ -1,9 +1,15 @@ import { get } from 'lodash' +import request, { create } from 'pubsweet-client/src/helpers/api' import { withJournal } from 'xpub-journal' import { login } from 'pubsweet-component-xpub-authentication/src/redux/login' import { SubmissionError } from 'redux-form' -import { create } from 'pubsweet-client/src/helpers/api' -import { compose, withState, withProps, withHandlers } from 'recompose' +import { + compose, + withState, + withProps, + withHandlers, + lifecycle, +} from 'recompose' import SignUpInvitation from './SignUpInvitationForm' @@ -22,7 +28,7 @@ const loginUser = (dispatch, values, history) => } }) -const confirmUser = (email, token) => (values, dispatch, { history }) => { +const confirmUser = (email, token, history) => (values, dispatch) => { const request = { ...values, email, token } if (values) { return create('/users/invite/password/reset', request) @@ -55,6 +61,16 @@ export default compose( withHandlers({ nextStep: ({ changeStep }) => () => changeStep(step => step + 1), prevStep: ({ changeStep }) => () => changeStep(step => step - 1), - submitConfirmation: ({ email, token }) => confirmUser(email, token), + submitConfirmation: ({ email, token, history }) => + confirmUser(email, token, history), + }), + lifecycle({ + componentDidMount() { + const { email, token } = this.props + const encodedUri = `?email=${encodeURIComponent(email)}&token=${token}` + request(`/users/invite${encodedUri}`).then(res => { + this.setState({ initialValues: res }) + }) + }, }), )(SignUpInvitation) diff --git a/packages/components-faraday/src/components/SignUp/SignUpStep0.js b/packages/components-faraday/src/components/SignUp/SignUpStep0.js index 809423c82ce5bdc2d9028b39b2677a971e8b2f28..87209f9f12a9ca0b0bd02d1fec7c3ad0b03c6263 100644 --- a/packages/components-faraday/src/components/SignUp/SignUpStep0.js +++ b/packages/components-faraday/src/components/SignUp/SignUpStep0.js @@ -4,11 +4,11 @@ import { reduxForm } from 'redux-form' import { required } from 'xpub-validators' import { Button, ValidatedField, TextField, Menu } from '@pubsweet/ui' -const Step0 = ({ journal, handleSubmit }) => ( +const Step0 = ({ journal, handleSubmit, initialValues }) => ( <FormContainer onSubmit={handleSubmit}> <Row> <RowItem> - <Label> First name </Label> + <Label> First name* </Label> <ValidatedField component={TextField} name="firstName" @@ -16,27 +16,17 @@ const Step0 = ({ journal, handleSubmit }) => ( /> </RowItem> <RowItem> - <Label> Affiliation </Label> - <ValidatedField component={TextField} name="affiliation" /> - </RowItem> - </Row> - <Row> - <RowItem> - <Label> Middle name </Label> - <ValidatedField component={TextField} name="middleName" /> - </RowItem> - <RowItem> - <Label> Position </Label> + <Label> Affiliation* </Label> <ValidatedField component={TextField} - name="position" + name="affiliation" validate={[required]} /> </RowItem> </Row> <Row> <RowItem> - <Label> Last name </Label> + <Label> Last name* </Label> <ValidatedField component={TextField} name="lastName" @@ -44,7 +34,7 @@ const Step0 = ({ journal, handleSubmit }) => ( /> </RowItem> <RowItem> - <Label> Title </Label> + <Label> Title* </Label> <ValidatedField component={input => <Menu {...input} options={journal.title} />} name="title" @@ -63,6 +53,7 @@ const Step0 = ({ journal, handleSubmit }) => ( export default reduxForm({ form: 'signUpInvitation', destroyOnUnmount: false, + enableReinitialize: true, forceUnregisterOnUnmount: true, })(Step0) diff --git a/packages/xpub-faraday/app/config/journal/title.js b/packages/xpub-faraday/app/config/journal/title.js index d2829fac00346ecc4782359c6e290bc2cc053f88..0c31d9756cec7ead33a64a9d7fcfd555d9b5eddd 100644 --- a/packages/xpub-faraday/app/config/journal/title.js +++ b/packages/xpub-faraday/app/config/journal/title.js @@ -1,6 +1,26 @@ export default [ { - label: 'Prof.', + label: 'Mr', + value: 'mr', + }, + { + label: 'Mrs', + value: 'mrs', + }, + { + label: 'Miss', + value: 'miss', + }, + { + label: 'Ms', + value: 'ms', + }, + { + label: 'Dr', + value: 'dr', + }, + { + label: 'Professor', value: 'prof', }, ]