Skip to content
Snippets Groups Projects
AdminUsers.js 6.91 KiB
Newer Older
import React from 'react'
import { get, isEqual } from 'lodash'
import { connect } from 'react-redux'
import { actions } from 'pubsweet-client'
import { ConnectPage } from 'xpub-connect'
import { withJournal } from 'xpub-journal'
import { Icon, th } from '@pubsweet/ui'
import { withRouter, Link } from 'react-router-dom'
import styled, { withTheme } from 'styled-components'
import { compose, withState, withHandlers } from 'recompose'
import {
  withModal,
  ConfirmationModal,
} from 'pubsweet-component-modal/src/components'

import { Pagination } from './'
import { updateUserStatusModal } from './utils'
  email,
  firstName = '',
  lastName = '',
  isActive = true,
  toggleUserStatus,
  getStatusLabel,
    <td>{`${firstName} ${lastName}`}</td>
    <td>{email}</td>
    <td>{affiliation}</td>
      <Role>{`Author${isEqual(editorInChief, true) ? ', Editor in Chief' : ''}${
        isEqual(handlingEditor, true) ? ', Handling Editor' : ''
      }${isEqual(admin, true) ? ', Admin' : ''}`}</Role>
      <Tag>{getStatusLabel()}</Tag>
      <Action to={`/admin/users/edit/${id}`}>Edit</Action>
      <ActionButton onClick={toggleUserStatus}>
        {isActive ? 'Deactivate' : 'Reactivate'}
      </ActionButton>
    </TD>
Alexandru Munteanu's avatar
Alexandru Munteanu committed
  history,
  itemsPerPage,
  incrementPage,
  decrementPage,
  getStatusLabel,
  toggleUserStatus,
Alexandru Munteanu's avatar
Alexandru Munteanu committed
}) => {
  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>
Alexandru Munteanu's avatar
Alexandru Munteanu committed
          &nbsp; Add User
        </AddButton>
      </Header>
      <SubHeader>
        <Pagination
          decrementPage={decrementPage}
          hasMore={itemsPerPage * (page + 1) < users.length}
          incrementPage={incrementPage}
          itemsPerPage={itemsPerPage}
          maxLength={users.length}
          page={page}
        />
      </SubHeader>
Alexandru Munteanu's avatar
Alexandru Munteanu committed
      <Table>
        <thead>
          <tr>
            <td>Full name</td>
Alexandru Munteanu's avatar
Alexandru Munteanu committed
            <td>Affiliation</td>
            <td width="220">Roles</td>
Alexandru Munteanu's avatar
Alexandru Munteanu committed
            <td>Status</td>
            <td width="125" />
Alexandru Munteanu's avatar
Alexandru Munteanu committed
          </tr>
        </thead>
        <tbody>
          {slicedUsers.map(u => (
            <TableRow
              key={u.id}
              {...u}
              getStatusLabel={getStatusLabel(u)}
Alexandru Munteanu's avatar
Alexandru Munteanu committed
              roleOptions={journal.roles}
              toggleUserStatus={toggleUserStatus(u)}
Alexandru Munteanu's avatar
Alexandru Munteanu committed
          ))}
        </tbody>
      </Table>
    </div>
  )
}
export default compose(
  ConnectPage(() => [actions.getUsers()]),
  withRouter,
  withModal(props => ({
    modalComponent: ConfirmationModal,
  })),
  connect(state => ({ users: get(state, 'users.users') })),
Alexandru Munteanu's avatar
Alexandru Munteanu committed
  withState('itemsPerPage', 'setItemsPerPage', 20),
  withState('page', 'setPage', 0),
  withHandlers({
Alexandru Munteanu's avatar
Alexandru Munteanu committed
    incrementPage: ({ setPage, page, itemsPerPage, users }) => () => {
      if (page * itemsPerPage + itemsPerPage < users.length) {
        setPage(p => p + 1)
      }
    },
    decrementPage: ({ setPage }) => () => {
      setPage(p => (p > 0 ? p - 1 : p))
    },
    getStatusLabel: () => ({ isConfirmed, isActive = true }) => () => {
      if (isConfirmed) {
        return isActive ? 'Active' : 'Inactive'
      }
      return 'Invited'
    toggleUserStatus: ({
      dispatch,
      showModal,
      hideModal,
      setModalError,
    }) => user => () => {
      updateUserStatusModal({
        dispatch,
        showModal,
        hideModal,
        setModalError,
        user,
      })
    },
  }),
)(Users)

// #region styled-components
const AddButton = styled.button`
  align-items: center;
  background-color: ${th('backgroundColor')};
  border: none;
  cursor: pointer;
  font-family: ${th('fontInterface')};
  font-size: ${th('fontSizeBaseSmall')};
  text-align: left;

  &:active,
  &:focus {
    outline: none;
  }
  &:hover {
    opacity: 0.7;
  }
const BreadCrumbs = styled.div`
    color: ${th('colorPrimary')};
    cursor: pointer;
    margin-left: calc(${th('subGridUnit')}*2);
      padding: 0 calc(${th('subGridUnit')}*2);
      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 {
    align-items: center;
    > div {
      margin-right: calc(${th('subGridUnit')});
    }
    font-family: ${th('fontReading')};
    font-size: ${th('fontSizeBaseSmall')};
    margin-right: calc(${th('subGridUnit')});
  }
`

const Table = styled.table`
  border-spacing: 0;
  border-collapse: collapse;
    border-bottom: ${th('borderDefault')};
    font-family: ${th('fontReading')};
    font-size: ${th('fontSizeBaseSmall')};
    text-align: left;
  }
`

const Row = styled.tr`
  border-bottom: ${th('borderDefault')};
  color: ${th('colorPrimary')};
  font-family: ${th('fontReading')};
  font-size: ${th('fontSizeBaseSmall')};
  height: 40px;
  text-align: left;
    background-color: ${th('backgroundColorReverse')};
const Tag = styled.span`
  border: solid 1px #667080;
  font-family: ${th('fontReading')};
  font-size: ${th('fontSizeBaseSmall')};
  margin-right: calc(${th('subGridUnit')});
  padding: 2px calc(${th('subGridUnit')}*2);
  text-align: left;
  text-transform: uppercase;
const Role = styled.div`
  font-size: ${th('fontSizeBaseSmall')};
  text-align: left;
  text-transform: uppercase;
`

const Action = styled(Link)`
  color: ${th('colorPrimary')};
const TD = styled.td`
  display: inline-flex;
  width: 100%;
  min-height: 20px;
`
const ActionButton = styled.i`
  cursor: pointer;
  display: none;
  font-style: unset;
  text-decoration: underline;
  margin: 0 calc(${th('subGridUnit')} * 2);