-
Bogdan Cochior authored490b2cec
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
import React from 'react'
import { get, isEqual } from 'lodash'
import { connect } from 'react-redux'
import styled, { withTheme } from 'styled-components'
import { Icon, Menu, th } from '@pubsweet/ui'
import { actions } from 'pubsweet-client'
import { ConnectPage } from 'xpub-connect'
import { withJournal } from 'xpub-journal'
import { withRouter, Link } from 'react-router-dom'
import { compose, withState, withHandlers } from 'recompose'
import { Pagination } from './'
const TableRow = ({
toggleUser,
selected,
id,
email,
roles,
username,
title = '',
firstName = '',
lastName = '',
affiliation,
isConfirmed,
editorInChief,
handlingEditor,
admin,
roleOptions,
}) => (
<Row>
<td>
<Input checked={selected} onClick={toggleUser} type="checkbox" />
</td>
<td>{email}</td>
<td>{`${firstName} ${lastName}`}</td>
<td>{affiliation}</td>
<td>
<Role>{`Author${isEqual(editorInChief, true) ? ', Editor in Chief' : ''}${
isEqual(handlingEditor, true) ? ', Handling Editor' : ''
}${isEqual(admin, true) ? ', Admin' : ''}`}</Role>
</td>
<td>
<Tag>{isConfirmed ? 'Confirmed' : 'Invited'}</Tag>
</td>
<td>
<Action to={`/admin/users/edit/${id}`}>Edit</Action>
</td>
</Row>
)
const Users = ({
users,
toggleUser,
toggleAllUsers,
incrementPage,
decrementPage,
page,
itemsPerPage,
history,
journal,
theme,
}) => {
const slicedUsers = users.slice(
page * itemsPerPage,
itemsPerPage * (page + 1),
)
return (
<div>
<Header>
<BreadCrumbs>
<span>Admin Dashboard</span>
<span>Users</span>
</BreadCrumbs>
<AddButton
data-test="button-add-user"
onClick={() => history.push('/admin/users/add')}
>
<Icon color={theme.colorPrimary} size={3}>
plus-circle
</Icon>
Add User
</AddButton>
</Header>
<SubHeader>
<div>
<span>Bulk actions: </span>
<Menu
inline
onChange={value => value}
options={[
{ value: 'deactivate', label: 'Deactivate' },
{ value: 'activate', label: 'Activate' },
]}
value="activate"
/>
<Menu
inline
onChange={value => value}
options={[
{ value: 'sort', label: 'SORT' },
{ value: 'unsort', label: 'UNSORT' },
]}
value="sort"
/>
<Icon color={theme.colorPrimary} size={4}>
search
</Icon>
</div>
<Pagination
decrementPage={decrementPage}
hasMore={itemsPerPage * (page + 1) < users.length}
incrementPage={incrementPage}
itemsPerPage={itemsPerPage}
maxLength={users.length}
page={page}
/>
</SubHeader>
<Table>
<thead>
<tr>
<td>
<Input
checked={users.every(u => u.selected)}
onClick={toggleAllUsers}
type="checkbox"
/>
</td>
<td>Email</td>
<td>Full name</td>
<td>Affiliation</td>
<td width="220">Roles</td>
<td>Status</td>
<td width="50" />
</tr>
</thead>
<tbody>
{slicedUsers.map(u => (
<TableRow
key={u.id}
{...u}
roleOptions={journal.roles}
toggleUser={toggleUser(u)}
/>
))}
</tbody>
</Table>
</div>
)
}
export default compose(
ConnectPage(() => [actions.getUsers()]),
withRouter,
withJournal,
withTheme,
connect(state => ({ currentUsers: get(state, 'users.users') })),
withState('users', 'setUsers', props =>
props.currentUsers.map(u => ({ ...u, selected: false })),
),
withState('itemsPerPage', 'setItemsPerPage', 20),
withState('page', 'setPage', 0),
withHandlers({
incrementPage: ({ setPage, page, itemsPerPage, users }) => () => {
if (page * itemsPerPage + itemsPerPage < users.length) {
setPage(p => p + 1)
}
},
decrementPage: ({ setPage }) => () => {
setPage(p => (p > 0 ? p - 1 : p))
},
toggleUser: ({ setUsers }) => user => () => {
setUsers(prev =>
prev.map(u => (u.id === user.id ? { ...u, selected: !u.selected } : u)),
)
},
toggleAllUsers: ({ setUsers }) => () => {
setUsers(users => users.map(u => ({ ...u, selected: !u.selected })))
},
}),
)(Users)
// #region styled-components
const AddButton = styled.button`
align-items: center;
border: none;
cursor: pointer;
display: flex;
font-family: ${th('fontInterface')};
font-size: ${th('fontSizeBaseSmall')};
text-align: left;
color: ${th('colorPrimary')};
background-color: ${th('backgroundColor')};
&:active,
&:focus {
outline: none;
}
&:hover {
opacity: 0.7;
}
`
const Header = styled.div`
display: flex;
flex-direction: row;
align-items: center;
`
const BreadCrumbs = styled.div`
& span {
font-size: ${th('fontSizeBase')};
text-align: left;
color: ${th('colorPrimary')};
cursor: pointer;
margin-left: calc(${th('subGridUnit')}*2);
&:after {
content: '>';
padding: 0 calc(${th('subGridUnit')}*2);
}
&:last-child {
font-size: ${th('fontSizeBase')};
font-weight: bold;
&:after {
content: '';
}
}
`
const SubHeader = styled.div`
align-items: center;
border-bottom: ${th('borderDefault')};
display: flex;
flex-direction: row;
justify-content: space-between;
margin-top: ${th('gridUnit')};
padding-bottom: calc(${th('subGridUnit')}*2);
> div:first-child {
display: flex;
align-items: center;
> div {
margin-right: calc(${th('subGridUnit')});
}
}
span {
font-family: ${th('fontReading')};
font-size: ${th('fontSizeBaseSmall')};
text-align: left;
color: ${th('colorPrimary')};
margin-right: calc(${th('subGridUnit')});
}
`
const Table = styled.table`
border-spacing: 0;
border-collapse: collapse;
width: 100%;
& thead tr {
height: 40px;
border-bottom: ${th('borderDefault')};
font-family: ${th('fontReading')};
font-size: ${th('fontSizeBaseSmall')};
font-weight: bold;
text-align: left;
color: ${th('colorPrimary')};
}
`
const Row = styled.tr`
border-bottom: ${th('borderDefault')};
color: ${th('colorPrimary')};
font-family: ${th('fontReading')};
font-size: ${th('fontSizeBaseSmall')};
height: 40px;
text-align: left;
&:hover {
background-color: ${th('backgroundColorReverse')};
a {
display: block;
}
}
`
const Tag = styled.span`
border: solid 1px #667080;
text-transform: uppercase;
font-family: ${th('fontReading')};
font-size: ${th('fontSizeBaseSmall')};
font-weight: bold;
text-align: left;
color: ${th('colorPrimary')};
padding: 2px calc(${th('subGridUnit')}*2);
margin-right: calc(${th('subGridUnit')});
`
const Role = styled.div`
font-size: ${th('fontSizeBaseSmall')};
text-align: left;
color: ${th('colorPrimary')};
text-transform: uppercase;
line-height: 1.5;
`
const Action = styled(Link)`
color: ${th('colorPrimary')};
display: none;
`
const Input = styled.input`
height: 20px;
width: 20px;
`
// #endregion