diff --git a/packages/component-faraday-ui/src/ShadowedBox.js b/packages/component-faraday-ui/src/ShadowedBox.js
index f30f7ef8a6e2d26597b9ce402ec378c83672d54d..87c2e1ea56867aedf8e12771afd14488a400ccaf 100644
--- a/packages/component-faraday-ui/src/ShadowedBox.js
+++ b/packages/component-faraday-ui/src/ShadowedBox.js
@@ -9,12 +9,12 @@ const width = props => css`
   width: calc(${th('gridUnit')} * ${get(props, 'width', 50)});
 `
 
-export default styled.div.attrs({
-  pt: props => get(props, 'pt', 2),
-  pr: props => get(props, 'pr', 2),
-  pb: props => get(props, 'pb', 2),
-  pl: props => get(props, 'pl', 2),
-})`
+export default styled.div.attrs(props => ({
+  pt: get(props, 'pt', 2),
+  pr: get(props, 'pr', 2),
+  pb: get(props, 'pb', 2),
+  pl: get(props, 'pl', 2),
+}))`
   background-color: ${th('colorBackgroundHue')};
   border-radius: ${th('borderRadius')};
   box-shadow: ${th('boxShadow')};
diff --git a/packages/component-faraday-ui/src/modals/ValidatedMenuField.js b/packages/component-faraday-ui/src/ValidatedMenuField.js
similarity index 100%
rename from packages/component-faraday-ui/src/modals/ValidatedMenuField.js
rename to packages/component-faraday-ui/src/ValidatedMenuField.js
diff --git a/packages/component-faraday-ui/src/gridItems/Item.js b/packages/component-faraday-ui/src/gridItems/Item.js
index a784d8561d2aaa1ee4e87ab87559efbeb79d650c..3314a5eda1525e86d2f22412ccb9f5623a06d28b 100644
--- a/packages/component-faraday-ui/src/gridItems/Item.js
+++ b/packages/component-faraday-ui/src/gridItems/Item.js
@@ -5,9 +5,9 @@ import styled from 'styled-components'
 import { marginHelper, paddingHelper } from 'pubsweet-component-faraday-ui'
 
 /** @component */
-const Item = styled.div.attrs({
-  'data-test-id': props => props['data-test-id'] || 'item',
-})`
+const Item = styled.div.attrs(props => ({
+  'data-test-id': props['data-test-id'] || 'item',
+}))`
   align-items: ${({ alignItems }) => alignItems || 'initial'};
   display: flex;
   flex: ${({ flex }) => (isNumber(flex) ? flex : 1)};
diff --git a/packages/component-faraday-ui/src/gridItems/Row.js b/packages/component-faraday-ui/src/gridItems/Row.js
index 9885c26aa20d875994268925273dd173167d28a0..86bcafade7093e078865b478341eb64a03761a4b 100644
--- a/packages/component-faraday-ui/src/gridItems/Row.js
+++ b/packages/component-faraday-ui/src/gridItems/Row.js
@@ -5,9 +5,9 @@ import styled from 'styled-components'
 import { heightHelper, marginHelper, paddingHelper } from '../styledHelpers'
 
 /** @component */
-const Row = styled.div.attrs({
-  'data-test-id': props => props['data-test-id'] || 'row',
-})`
+const Row = styled.div.attrs(props => ({
+  'data-test-id': props['data-test-id'] || 'row',
+}))`
   align-items: ${props => get(props, 'alignItems', 'flex-start')};
   background-color: ${props => props.bgColor || 'transparent'};
   display: flex;
diff --git a/packages/component-faraday-ui/src/index.js b/packages/component-faraday-ui/src/index.js
index 2d3859c78c7a2eda863ea5cef314771995ffa6b6..6d5f3b1be3fa8bc2fe83cf3c2bff21242bb75160 100644
--- a/packages/component-faraday-ui/src/index.js
+++ b/packages/component-faraday-ui/src/index.js
@@ -51,6 +51,7 @@ export { default as EditorialReportCard } from './EditorialReportCard'
 export { default as ReviewerReportAuthor } from './ReviewerReportAuthor'
 export { default as PasswordValidation } from './PasswordValidation'
 export { default as MenuCountry } from './MenuCountry'
+export { default as ValidatedMenuField } from './ValidatedMenuField'
 
 export { SubmitRevision } from './submissionRevision'
 
diff --git a/packages/component-faraday-ui/src/modals/MyMenu.js b/packages/component-faraday-ui/src/modals/MyMenu.js
deleted file mode 100644
index 9fe37eb41ed0d80cc4c053850f8eecc5560a74cd..0000000000000000000000000000000000000000
--- a/packages/component-faraday-ui/src/modals/MyMenu.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React, { Fragment } from 'react'
-import { compose, withStateHandlers } from 'recompose'
-
-const MyMenu = ({ open, toggleMenu, field, form }) => (
-  <div onClick={toggleMenu}>
-    Click to open
-    {open && (
-      <Fragment>
-        <span onClick={() => form.setFieldValue(field.name, 1)}>Option 1</span>
-        <span onClick={() => form.setFieldValue(field.name, 2)}>Option 2</span>
-        <span onClick={() => form.setFieldValue(field.name, 3)}>Option 3</span>
-      </Fragment>
-    )}
-  </div>
-)
-
-export default compose(
-  withStateHandlers(
-    { open: false },
-    {
-      toggleMenu: ({ open }) => () => ({
-        open: !open,
-      }),
-    },
-  ),
-)(MyMenu)
diff --git a/packages/component-faraday-ui/src/modals/OpenModal.js b/packages/component-faraday-ui/src/modals/OpenModal.js
index d575f9a6ad599ae1512a35d76d59f8f511030ec9..6e5bc43d21ada388fc1c964b2e15fe8daae59a6c 100644
--- a/packages/component-faraday-ui/src/modals/OpenModal.js
+++ b/packages/component-faraday-ui/src/modals/OpenModal.js
@@ -17,6 +17,7 @@ const OpenModal = ({
   content,
   modalKey,
 }) => <div>{children(showModal)}</div>
+
 const selectModalComponent = props => {
   if (props.component) {
     return {
@@ -54,15 +55,15 @@ OpenModal.propTypes = {
   modalKey: PropTypes.string,
 }
 OpenModal.defaultProps = {
-  title: undefined,
-  subtitle: undefined,
+  title: '',
+  subtitle: '',
   confirmText: 'OK',
   cancelText: 'Cancel',
-  onCancel: undefined,
-  onConfirm: undefined,
-  onClose: undefined,
-  content: undefined,
-  modalKey: undefined,
+  onCancel: () => {},
+  onConfirm: () => {},
+  onClose: () => {},
+  content: null,
+  modalKey: 'aModal',
 }
 
 export default compose(
diff --git a/packages/component-faraday-ui/src/modals/index.js b/packages/component-faraday-ui/src/modals/index.js
index f45d2261541326dae4e34a5c8abed68aa62f2c56..fb4d8875a5d9973606474a543a65321bb1347e26 100644
--- a/packages/component-faraday-ui/src/modals/index.js
+++ b/packages/component-faraday-ui/src/modals/index.js
@@ -1,4 +1,3 @@
 export { default as OpenModal } from './OpenModal'
-export { default as FormModal } from './FormModal'
 export { default as MultiAction } from './MultiAction'
 export { default as SingleActionModal } from './SingleActionModal'
diff --git a/packages/component-fixture-manager/src/helpers/Model.js b/packages/component-fixture-manager/src/helpers/Model.js
index b59599b91db87ddedbc6311ce2a98ba94fb9363b..1f292351b8d27378cb54308a8d023b5b5eeabfd6 100644
--- a/packages/component-fixture-manager/src/helpers/Model.js
+++ b/packages/component-fixture-manager/src/helpers/Model.js
@@ -26,6 +26,9 @@ const build = fixtures => {
   UserMock.updateProperties = jest.fn(user =>
     updatePropertiesMock(user, 'users'),
   )
+  UserMock.update = jest.fn((id, input) => input)
+  UserMock.fetchOne = jest.fn(user => user)
+  UserMock.create = jest.fn(input => input)
 
   TeamMock.find = jest.fn(id => findMock(id, 'teams', fixtures))
   TeamMock.updateProperties = jest.fn(team =>
diff --git a/packages/components-faraday/src/components/Admin/AdminRoute.js b/packages/component-user/app/AdminRoute.js
similarity index 92%
rename from packages/components-faraday/src/components/Admin/AdminRoute.js
rename to packages/component-user/app/AdminRoute.js
index 4b95cac85c1e8f52f6384f7fad2c47d8d25c0cb1..b243ce527f1a372210da8b1cfa73bbf3a5502942 100644
--- a/packages/components-faraday/src/components/Admin/AdminRoute.js
+++ b/packages/component-user/app/AdminRoute.js
@@ -2,8 +2,8 @@ import React from 'react'
 import { get } from 'lodash'
 import { compose } from 'recompose'
 import { connect } from 'react-redux'
-import { Redirect, withRouter, Route } from 'react-router-dom'
 import { AuthenticatedComponent } from 'pubsweet-client'
+import { Redirect, withRouter, Route } from 'react-router-dom'
 
 const AdminRoute = ({
   currentUser,
@@ -11,7 +11,7 @@ const AdminRoute = ({
   component: Component,
   ...rest
 }) => {
-  const isAdmin = get(currentUser, 'user.admin')
+  const isAdmin = get(currentUser, 'user.admin', false)
   return (
     <Route
       {...rest}
diff --git a/packages/component-faraday-ui/src/modals/FormModal.js b/packages/component-user/app/components/AdminUserForm.js
similarity index 53%
rename from packages/component-faraday-ui/src/modals/FormModal.js
rename to packages/component-user/app/components/AdminUserForm.js
index a139df8e733baa7732263b383c066ba776220ed0..50a314f428bc0251880441d6a49f18d591d14f96 100644
--- a/packages/component-faraday-ui/src/modals/FormModal.js
+++ b/packages/component-user/app/components/AdminUserForm.js
@@ -4,66 +4,118 @@ import { Formik } from 'formik'
 import styled from 'styled-components'
 import { th } from '@pubsweet/ui-toolkit'
 import { required } from 'xpub-validators'
-import { H2, Button, TextField, ValidatedFieldFormik } from '@pubsweet/ui'
 import { compose, setDisplayName, withHandlers, withProps } from 'recompose'
+import {
+  H2,
+  Button,
+  Spinner,
+  TextField,
+  ValidatedFieldFormik,
+} from '@pubsweet/ui'
 import {
   Row,
   Item,
+  Text,
   Label,
   IconButton,
   RowOverrideAlert,
   ItemOverrideAlert,
+  ValidatedMenuField,
   withRoles,
+  withFetching,
   withCountries,
 } from 'pubsweet-component-faraday-ui'
 
-import ValidatedMenuField from './ValidatedMenuField'
-import ValidatedCheckboxField from './ValidatedCheckboxField'
+// #region helpers
+const setInitialRole = a => {
+  if (get(a, 'admin', false)) {
+    return 'admin'
+  }
+  if (get(a, 'handlingEditor', false)) {
+    return 'handlingEditor'
+  }
+  if (get(a, 'editorInChief', false)) {
+    return 'editorInChief'
+  }
+  return 'author'
+}
+
+const validate = values => {
+  const errors = {}
+
+  if (get(values, 'email', '') === '') {
+    errors.email = 'Required'
+  }
+
+  return errors
+}
+// #endregion
 
 const FormModal = ({
   edit,
-  user,
   roles,
   title,
   titles,
   onClose,
-  subtitle,
+  onSubmit,
   onConfirm,
   countries,
+  isFetching,
+  fetchingError,
+  initialValues,
   confirmText = 'OK',
   cancelText = 'Cancel',
-  onSubmit,
-  initialValues,
+  user,
 }) => (
   <Root>
     <IconButton icon="x" onClick={onClose} right={5} secondary top={5} />
     <H2>{title}</H2>
-    <Formik initialValues={initialValues} onSubmit={onSubmit}>
-      {({ handleSubmit }) => (
+    {edit && (
+      <Text mb={1} secondary>
+        {get(user, 'email', '')}
+      </Text>
+    )}
+    <Formik
+      initialValues={initialValues}
+      onSubmit={onSubmit}
+      validate={validate}
+    >
+      {({ handleSubmit, ...rest }) => (
         <Fragment>
-          <Row alignItems="baseline" mb={1} mt={1}>
-            <Item mr={1} vertical>
-              <Label required>Email</Label>
-              <ValidatedFieldFormik
-                component={TextField}
-                name="email"
-                validate={[required]}
-              />
-            </Item>
-            <ItemOverrideAlert ml={1} vertical>
-              <Label required>Role</Label>
-              <ValidatedMenuField name="role" options={roles} />
-            </ItemOverrideAlert>
-          </Row>
+          {!edit && (
+            <Row alignItems="baseline" mb={1} mt={1}>
+              <ItemOverrideAlert mr={1} vertical>
+                <Label required>Email</Label>
+                <ValidatedFieldFormik
+                  component={TextField}
+                  inline
+                  name="email"
+                  validate={[required]}
+                />
+              </ItemOverrideAlert>
+              <ItemOverrideAlert ml={1} vertical>
+                <Label required>Role</Label>
+                <ValidatedMenuField name="role" options={roles} />
+              </ItemOverrideAlert>
+            </Row>
+          )}
 
           <Row mb={2}>
             <Item mr={1} vertical>
               <Label>First Name</Label>
-              <ValidatedFieldFormik component={TextField} name="firstName" />
+              <ValidatedFieldFormik
+                component={TextField}
+                inline
+                name="firstName"
+              />
             </Item>
             <Item ml={1} vertical>
               <Label>Last Name</Label>
-              <ValidatedFieldFormik component={TextField} name="lastName" />
+              <ValidatedFieldFormik
+                component={TextField}
+                inline
+                name="lastName"
+              />
             </Item>
           </Row>
 
@@ -78,68 +130,48 @@ const FormModal = ({
             </ItemOverrideAlert>
           </RowOverrideAlert>
 
-          <Row mb={!edit && 3}>
-            <Item vertical>
+          <Row mb={3}>
+            {edit && (
+              <ItemOverrideAlert mr={1} vertical>
+                <Label required>Role</Label>
+                <ValidatedMenuField name="role" options={roles} />
+              </ItemOverrideAlert>
+            )}
+            <Item ml={edit && 1} vertical>
               <Label>Affiliation</Label>
-              <ValidatedFieldFormik component={TextField} name="affiliation" />
+              <ValidatedFieldFormik
+                component={TextField}
+                inline
+                name="affiliation"
+              />
             </Item>
           </Row>
 
-          {edit && (
-            <RowOverrideAlert mb={3}>
-              <Item>
-                <ValidatedCheckboxField
-                  label="Editor in Chief"
-                  name="editorInChief"
-                />
-              </Item>
-              <Item>
-                <ValidatedCheckboxField label="Admin" name="admin" />
-              </Item>
-              <Item>
-                <ValidatedCheckboxField
-                  label="Handling Editor"
-                  name="handlingEditor"
-                />
-              </Item>
-            </RowOverrideAlert>
+          {fetchingError && (
+            <Row mb={1}>
+              <Text error>{fetchingError}</Text>
+            </Row>
           )}
 
-          <Row>
-            <Button onClick={onClose}>Cancel</Button>
-            <Button onClick={handleSubmit} primary>
-              {confirmText}
-            </Button>
-          </Row>
+          {isFetching ? (
+            <Spinner />
+          ) : (
+            <Row>
+              <Button onClick={onClose}>Cancel</Button>
+              <Button onClick={handleSubmit} primary>
+                {confirmText}
+              </Button>
+            </Row>
+          )}
         </Fragment>
       )}
     </Formik>
   </Root>
 )
 
-// #region FormHelpers
-const setInitialRole = a => {
-  if (get(a, 'handlingEditor')) {
-    return 'handlingEditor'
-  }
-  if (get(a, 'editorInChief')) {
-    return 'editorInChief'
-  }
-  return 'author'
-}
-
-const parseValues = ({ email, role, ...rest }) => ({
-  email,
-  username: email,
-  admin: role === 'admin',
-  editorInChief: role === 'editorInChief',
-  handlingEditor: role === 'handlingEditor',
-  ...rest,
-})
-// #endregion
-
 export default compose(
   withRoles,
+  withFetching,
   withCountries,
   withProps(({ user, edit }) => ({
     initialValues: {
@@ -152,7 +184,7 @@ export default compose(
   withHandlers({
     onSubmit: ({ onSubmit, ...props }) => (values, formProps) => {
       if (typeof onSubmit === 'function') {
-        onSubmit(parseValues(values), { formProps, props })
+        onSubmit(values, { ...formProps, ...props })
       }
     },
     onClose: ({ onCancel, ...props }) => () => {
@@ -163,7 +195,7 @@ export default compose(
     },
   }),
 
-  setDisplayName('FormModal'),
+  setDisplayName('AdminUserForm'),
 )(FormModal)
 
 // #region styles
diff --git a/packages/component-user/app/components/OpenStatusModal.js b/packages/component-user/app/components/OpenStatusModal.js
new file mode 100644
index 0000000000000000000000000000000000000000..f97ea6a7ec8b6c945b9959cc0f5735fdab35124f
--- /dev/null
+++ b/packages/component-user/app/components/OpenStatusModal.js
@@ -0,0 +1,46 @@
+import React from 'react'
+import { Button } from '@pubsweet/ui'
+import styled from 'styled-components'
+import { th } from '@pubsweet/ui-toolkit'
+import { withHandlers, compose, setDisplayName } from 'recompose'
+import { OpenModal, withFetching } from 'pubsweet-component-faraday-ui'
+
+const OpenStatusModal = ({
+  onConfirm,
+  isFetching,
+  user: { id, firstName = '', lastName = '', isActive },
+}) => (
+  <OpenModal
+    isFetching={isFetching}
+    modalKey={`deactivate-${id}`}
+    onConfirm={onConfirm}
+    subtitle={`${firstName} ${lastName}`}
+    title={`Are you sure you want to ${
+      !isActive ? 'activate' : 'deactivate'
+    } user?`}
+  >
+    {showModal => (
+      <ActivateButton ml={1} mr={1} onClick={showModal} size="small">
+        {!isActive ? 'ACTIVATE' : 'DEACTIVATE'}
+      </ActivateButton>
+    )}
+  </OpenModal>
+)
+
+export default compose(
+  withFetching,
+  withHandlers({
+    onConfirm: ({ user, onConfirm, ...props }) => modalProps => {
+      if (typeof onConfirm === 'function') {
+        onConfirm(user, { ...props, ...modalProps })
+      }
+    },
+  }),
+  setDisplayName('AdminStatusModal'),
+)(OpenStatusModal)
+
+// #region styles
+const ActivateButton = styled(Button)`
+  width: calc(${th('gridUnit')} * 10);
+`
+// #endregion
diff --git a/packages/components-faraday/src/components/Admin/OpenUserForm.js b/packages/component-user/app/components/OpenUserForm.js
similarity index 89%
rename from packages/components-faraday/src/components/Admin/OpenUserForm.js
rename to packages/component-user/app/components/OpenUserForm.js
index 63b03c13f5c0ee40da1784ea55961fd185c89716..985c02360c081f1f71128f3c398baa6d57e473bd 100644
--- a/packages/components-faraday/src/components/Admin/OpenUserForm.js
+++ b/packages/component-user/app/components/OpenUserForm.js
@@ -6,10 +6,12 @@ import {
   IconButton,
 } from 'pubsweet-component-faraday-ui'
 
+import AdminUserForm from './AdminUserForm'
+
 const OpenUserForm = ({ edit, user, onSubmit, modalKey }) => (
   <OpenModal
+    component={AdminUserForm}
     edit={edit}
-    formModal
     modalKey={modalKey}
     onSubmit={onSubmit}
     user={user}
@@ -18,9 +20,9 @@ const OpenUserForm = ({ edit, user, onSubmit, modalKey }) => (
       edit ? (
         <IconButton
           fontIcon="editIcon"
-          paddingBottom={1.5}
           iconSize={2}
           onClick={showModal}
+          paddingBottom={1.5}
           pt={1 / 2}
         />
       ) : (
diff --git a/packages/component-user/app/components/index.js b/packages/component-user/app/components/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..900efc4da7fce9eeff226effb58fff0f6e4305f6
--- /dev/null
+++ b/packages/component-user/app/components/index.js
@@ -0,0 +1,3 @@
+export { default as AdminUserForm } from './AdminUserForm'
+export { default as OpenStatusModal } from './OpenStatusModal'
+export { default as OpenUserForm } from './OpenUserForm'
diff --git a/packages/component-user/app/graphql/fragments.js b/packages/component-user/app/graphql/fragments.js
new file mode 100644
index 0000000000000000000000000000000000000000..36282982757d503eeb0a8da401ea3693c59a6ecb
--- /dev/null
+++ b/packages/component-user/app/graphql/fragments.js
@@ -0,0 +1,18 @@
+import gql from 'graphql-tag'
+
+export const userFragment = gql`
+  fragment userDetails on User {
+    id
+    admin
+    email
+    title
+    country
+    username
+    lastName
+    isActive
+    firstName
+    affiliation
+    editorInChief
+    handlingEditor
+  }
+`
diff --git a/packages/component-user/app/graphql/index.js b/packages/component-user/app/graphql/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..cf105b664973c3d29668b05ebce1ccc06b981c21
--- /dev/null
+++ b/packages/component-user/app/graphql/index.js
@@ -0,0 +1,6 @@
+import * as fragments from './fragments'
+import * as queries from './queries'
+import * as mutations from './mutations'
+
+export { fragments, queries, mutations }
+export { default } from './withUsersGQL'
diff --git a/packages/component-user/app/graphql/mutations.js b/packages/component-user/app/graphql/mutations.js
new file mode 100644
index 0000000000000000000000000000000000000000..fed993f4c47abed820d098295348c30adb5e5cbb
--- /dev/null
+++ b/packages/component-user/app/graphql/mutations.js
@@ -0,0 +1,30 @@
+import gql from 'graphql-tag'
+
+import { userFragment } from './fragments'
+
+export const addUserAsAdmin = gql`
+  mutation addUserAsAdmin($input: UserInput!) {
+    addUserAsAdmin(input: $input) {
+      ...userDetails
+    }
+  }
+  ${userFragment}
+`
+
+export const editUserAsAdmin = gql`
+  mutation editUserAsAdmin($id: ID!, $input: UserInput!) {
+    editUserAsAdmin(id: $id, input: $input) {
+      ...userDetails
+    }
+  }
+  ${userFragment}
+`
+
+export const activateUserAsAdmin = gql`
+  mutation activateUserAsAdmin($id: ID!, $input: ActivateUserInput) {
+    activateUserAsAdmin(id: $id, input: $input) {
+      ...userDetails
+    }
+  }
+  ${userFragment}
+`
diff --git a/packages/component-user/app/graphql/queries.js b/packages/component-user/app/graphql/queries.js
new file mode 100644
index 0000000000000000000000000000000000000000..9e65b0e68fb74d888b2d7d82c1d0929977bbf979
--- /dev/null
+++ b/packages/component-user/app/graphql/queries.js
@@ -0,0 +1,12 @@
+import gql from 'graphql-tag'
+
+import { userFragment } from './fragments'
+
+export const getUsers = gql`
+  {
+    users {
+      ...userDetails
+    }
+  }
+  ${userFragment}
+`
diff --git a/packages/component-user/app/graphql/withUsersGQL.js b/packages/component-user/app/graphql/withUsersGQL.js
new file mode 100644
index 0000000000000000000000000000000000000000..aceee93b7866cb01e3d55256cdad7b479723ee71
--- /dev/null
+++ b/packages/component-user/app/graphql/withUsersGQL.js
@@ -0,0 +1,24 @@
+import { graphql } from 'react-apollo'
+import { compose, withProps } from 'recompose'
+
+import * as queries from './queries'
+import * as mutations from './mutations'
+
+export default compose(
+  graphql(queries.getUsers),
+  graphql(mutations.addUserAsAdmin, {
+    name: 'addUser',
+    options: {
+      refetchQueries: [{ query: queries.getUsers }],
+    },
+  }),
+  graphql(mutations.editUserAsAdmin, {
+    name: 'updateUser',
+  }),
+  graphql(mutations.activateUserAsAdmin, {
+    name: 'activateUser',
+  }),
+  withProps(({ data }) => ({
+    users: data.users,
+  })),
+)
diff --git a/packages/component-user/app/index.js b/packages/component-user/app/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..5cf2fee3273bda71f3524edd593d282797a0802b
--- /dev/null
+++ b/packages/component-user/app/index.js
@@ -0,0 +1,3 @@
+export { default as AdminRoute } from './AdminRoute'
+export { default as AdminUsers } from './pages/AdminUsers'
+export { default as AdminDashboard } from './pages/AdminDashboard'
diff --git a/packages/components-faraday/src/components/Admin/AdminDashboard.js b/packages/component-user/app/pages/AdminDashboard.js
similarity index 100%
rename from packages/components-faraday/src/components/Admin/AdminDashboard.js
rename to packages/component-user/app/pages/AdminDashboard.js
diff --git a/packages/components-faraday/src/components/Admin/AdminUsers.js b/packages/component-user/app/pages/AdminUsers.js
similarity index 70%
rename from packages/components-faraday/src/components/Admin/AdminUsers.js
rename to packages/component-user/app/pages/AdminUsers.js
index a28c576d7a4106e362ba3d6e5bbc2404f7941260..a76b5689c17a806f9e582461d2b9ade953cb5770 100644
--- a/packages/components-faraday/src/components/Admin/AdminUsers.js
+++ b/packages/component-user/app/pages/AdminUsers.js
@@ -1,6 +1,5 @@
 import React, { Fragment } from 'react'
 import { get } from 'lodash'
-import { Button } from '@pubsweet/ui'
 import styled from 'styled-components'
 import { th } from '@pubsweet/ui-toolkit'
 import { withJournal } from 'xpub-journal'
@@ -11,35 +10,27 @@ import {
   Text,
   Item,
   Label,
-  OpenModal,
   Pagination,
   ActionLink,
-  handleError,
-  withFetching,
   withPagination,
 } from 'pubsweet-component-faraday-ui'
 
-import { updateUserStatus } from './utils'
-import { OpenUserForm, withUsersGQL } from './'
+import withUsersGQL from '../graphql'
+import { OpenUserForm, OpenStatusModal } from '../components'
 
 const Users = ({
   page,
   users,
-  theme,
   history,
   journal,
-  getUsers,
-  isFetching,
+  addUser,
+  updateUser,
   getUserName,
   getUserRoles,
   itemsPerPage,
-  deactivateUser,
   getStatusLabel,
   paginatedItems,
   toggleUserStatus,
-  //
-  addUser,
-  updateUser,
   ...rest
 }) => (
   <Fragment>
@@ -48,9 +39,9 @@ const Users = ({
         <ActionLink
           data-test-id="go-to-dashboard"
           fontIcon="arrowLeft"
-          paddingBottom={1.2}
           mr={2}
           onClick={history.goBack}
+          paddingBottom={1.2}
         >
           Admin Dashboard
         </ActionLink>
@@ -108,26 +99,8 @@ const Users = ({
                   onSubmit={updateUser}
                   user={user}
                 />
-                <OpenModal
-                  isFetching={isFetching}
-                  modalKey={`deactivate-${user.id}`}
-                  onConfirm={deactivateUser(user)}
-                  subtitle={`${user.firstName} ${user.lastName}`}
-                  title={`Are you sure to ${
-                    !user.isActive ? 'activate' : 'deactivate'
-                  } user?`}
-                >
-                  {showModal => (
-                    <ActivateButton
-                      ml={1}
-                      mr={1}
-                      onClick={showModal}
-                      size="small"
-                    >
-                      {!user.isActive ? 'ACTIVATE' : 'DEACTIVATE'}
-                    </ActivateButton>
-                  )}
-                </OpenModal>
+
+                <OpenStatusModal onConfirm={toggleUserStatus} user={user} />
               </Item>
             </HiddenCell>
           </UserRow>
@@ -139,7 +112,6 @@ const Users = ({
 
 export default compose(
   withJournal,
-  withFetching,
   withUsersGQL,
   withProps(({ journal: { roles = {} }, users }) => ({
     roles: Object.keys(roles),
@@ -154,20 +126,86 @@ export default compose(
       }
       return isConfirmed ? 'ACTIVE' : 'INVITED'
     },
-    deactivateUser: ({ setFetching, getUsers }) => user => ({
-      hideModal,
-      setModalError,
-    }) => {
+    addUser: ({ addUser }) => (
+      {
+        __typename,
+        id,
+        admin,
+        isActive,
+        editorInChief,
+        handlingEditor,
+        ...input
+      },
+      { setFetching, hideModal, setError },
+    ) => {
+      setFetching(true)
+      addUser({
+        variables: {
+          input: {
+            ...input,
+            username: input.email,
+          },
+        },
+      })
+        .then(() => {
+          setFetching(false)
+          hideModal()
+        })
+        .catch(e => {
+          setFetching(false)
+          setError(e.message)
+        })
+    },
+    updateUser: ({ updateUser }) => (
+      {
+        __typename,
+        id,
+        admin,
+        isActive,
+        handlingEditor,
+        editorInChief,
+        ...input
+      },
+      { setFetching, hideModal, setError },
+    ) => {
+      setFetching(true)
+      updateUser({
+        variables: {
+          id,
+          input,
+        },
+      })
+        .then(() => {
+          setFetching(false)
+          hideModal()
+        })
+        .catch(e => {
+          setFetching(false)
+          setError(e.message)
+        })
+    },
+    toggleUserStatus: ({ activateUser }) => (
+      { id, email, username, isActive },
+      { setFetching, hideModal, setModalError },
+    ) => {
       setFetching(true)
-      updateUserStatus(user)
+      activateUser({
+        variables: {
+          id,
+          input: {
+            email,
+            username,
+            isActive,
+          },
+        },
+      })
         .then(() => {
           setFetching(false)
-          getUsers()
           hideModal()
         })
-        .catch(err => {
+        .catch(e => {
           setFetching(false)
-          handleError(setModalError)(err)
+          setModalError(e.message)
         })
     },
     getUserName: () => user => {
@@ -215,7 +253,3 @@ const UserRow = styled.tr`
     }
   }
 `
-const ActivateButton = styled(Button)`
-  width: 90px;
-`
-// #endregion
diff --git a/packages/component-user/index.js b/packages/component-user/index.js
index 307f862c05b48216eedc7514a3a76bf38f06eb11..c9b9c509b1ff10cbe0cd4af9b5e99c83429a9450 100644
--- a/packages/component-user/index.js
+++ b/packages/component-user/index.js
@@ -1,10 +1,7 @@
-const resolvers = require('./resolvers')
-const typeDefs = require('./typeDefs')
-const model = require('./user')
+const resolvers = require('./server/resolvers')
+const typeDefs = require('./server/typeDefs')
 
 module.exports = {
-  model,
-  modelName: 'User',
-  resolvers,
   typeDefs,
+  resolvers,
 }
diff --git a/packages/component-user/package.json b/packages/component-user/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..fe634328db1f0bff247365e65866c259ecdbd65d
--- /dev/null
+++ b/packages/component-user/package.json
@@ -0,0 +1,51 @@
+{
+  "name": "pubsweet-component-user",
+  "version": "0.0.1",
+  "description": "User component for Hindawi peer review app.",
+  "license": "MIT",
+  "author": "Collaborative Knowledge Foundation",
+  "files": ["src"],
+  "main": "index.js",
+  "scripts": {
+    "test": "jest",
+    "docs":
+      "./node_modules/.bin/apidoc -e \"(node_modules|public)\" -o public/apidoc",
+    "open-docs": "open public/apidoc/index.html"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://gitlab.coko.foundation/xpub/xpub-faraday",
+    "path": "component-user"
+  },
+  "dependencies": {
+    "@pubsweet/ui": "^9.0.3",
+    "@pubsweet/ui-toolkit": "^2.0.3",
+    "chance": "^1.0.13",
+    "formik": "^1.4.0",
+    "graphql-tag": "^2.10.0",
+    "lodash": "^4.17.11",
+    "pubsweet-client": "^7.0.0",
+    "pubsweet-server": "^10.0.0",
+    "pubsweet-component-helper-service": "0.0.1",
+    "react": "^16.6.0",
+    "react-apollo": "^2.3.2",
+    "react-dom": "^16.6.0",
+    "react-redux": "^5.0.2",
+    "react-router-dom": "^4.2.2",
+    "recompose": "^0.30.0",
+    "styled-components": "^4.1.2",
+    "xpub-validators": "^0.0.6"
+  },
+  "devDependencies": {
+    "apidoc": "^0.17.6",
+    "jest": "^23.6.0",
+    "supertest": "^3.0.0"
+  },
+  "jest": {
+    "verbose": true,
+    "testRegex": "/server/.*.test.js$"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
+}
diff --git a/packages/component-user/resolvers.js b/packages/component-user/resolvers.js
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/packages/component-user/server/notifications/emailCopy.js b/packages/component-user/server/notifications/emailCopy.js
new file mode 100644
index 0000000000000000000000000000000000000000..b8c618a83b4b1a6cddea14520e5aa142adfc1cf4
--- /dev/null
+++ b/packages/component-user/server/notifications/emailCopy.js
@@ -0,0 +1,33 @@
+const config = require('config')
+
+const journalName = config.get('journal.name')
+const getEmailCopy = ({ emailType, role }) => {
+  let paragraph
+  let hasIntro = true
+  let hasSignature = true
+  switch (emailType) {
+    case 'user-signup':
+      paragraph = `Thank you for creating an account on Hindawi’s review system.
+        To submit a manuscript and access your dashboard, please confirm your account by clicking on the link below.`
+      break
+    case 'user-added-by-admin':
+      hasIntro = false
+      hasSignature = false
+      paragraph = `You have been invited to join Hindawi as a ${role}.
+        Please confirm your account and set your account details by clicking on the link below.`
+      break
+    case 'he-added-by-admin':
+      paragraph = `You have been invited to become an Academic Editor for the journal ${journalName}.
+        To begin performing your editorial duties, you will need to create an account on Hindawi’s review system.<br/><br/>
+        Please confirm your account details by clicking on the link below.`
+      break
+    default:
+      throw new Error(`The ${emailType} email type is not defined.`)
+  }
+
+  return { paragraph, hasLink: true, hasIntro, hasSignature }
+}
+
+module.exports = {
+  getEmailCopy,
+}
diff --git a/packages/component-user/server/notifications/notification.js b/packages/component-user/server/notifications/notification.js
new file mode 100644
index 0000000000000000000000000000000000000000..c32282a6e7cbd60bd91ec4eb24add9254fa4fc95
--- /dev/null
+++ b/packages/component-user/server/notifications/notification.js
@@ -0,0 +1,58 @@
+const config = require('config')
+const Email = require('@pubsweet/component-email-templating')
+const { services } = require('pubsweet-component-helper-service')
+
+const { getEmailCopy } = require('./emailCopy')
+
+const { name: journalName, staffEmail } = config.get('journal')
+const unsubscribeSlug = config.get('unsubscribe.url')
+
+class Notification {
+  constructor(user) {
+    this.user = user
+  }
+
+  async notifyUserAddedByAdmin(role) {
+    const resetPath = config.get('invite-reset-password.url')
+    const { user } = this
+    const baseUrl = config.get('pubsweet-client.baseUrl')
+    const emailType =
+      role === 'Handling Editor' ? 'he-added-by-admin' : 'user-added-by-admin'
+
+    const { paragraph, ...bodyProps } = getEmailCopy({
+      role,
+      emailType,
+    })
+
+    const email = new Email({
+      type: 'user',
+      fromEmail: `${journalName} <${staffEmail}>`,
+      toUser: {
+        email: user.email,
+      },
+      content: {
+        subject: 'Confirm your account',
+        ctaLink: services.createUrl(baseUrl, resetPath, {
+          email: user.email,
+          token: user.accessTokens.passwordReset,
+          firstName: user.firstName,
+          lastName: user.lastName,
+          affiliation: user.affiliation,
+          title: user.title,
+          country: user.country,
+        }),
+        ctaText: 'CONFIRM ACCOUNT',
+        paragraph,
+        unsubscribeLink: services.createUrl(baseUrl, unsubscribeSlug, {
+          id: user.id,
+          token: user.accessTokens.unsubscribe,
+        }),
+      },
+      bodyProps,
+    })
+
+    return email.sendEmail()
+  }
+}
+
+module.exports = Notification
diff --git a/packages/component-user/server/resolvers.js b/packages/component-user/server/resolvers.js
new file mode 100644
index 0000000000000000000000000000000000000000..dd914063955f0aeee31ef236bb7dd79e82f63aa7
--- /dev/null
+++ b/packages/component-user/server/resolvers.js
@@ -0,0 +1,41 @@
+const Notification = require('./notifications/notification')
+
+const { parseUserFromAdmin } = require('./user')
+
+const {
+  editUser,
+  activateUser,
+  deactivateUser,
+  createUserAsAdmin,
+} = require('./use-cases')
+
+const resolvers = {
+  Mutation: {
+    async addUserAsAdmin(_, { input }, ctx) {
+      return createUserAsAdmin
+        .initialize({
+          ctx,
+          Notification,
+          User: ctx.connectors.User,
+        })
+        .execute({ input: parseUserFromAdmin(input) })
+    },
+    async editUserAsAdmin(_, { id, input }, ctx) {
+      return editUser
+        .initialize({ User: ctx.connectors.User, ctx })
+        .execute({ id, input: parseUserFromAdmin(input) })
+    },
+    async toggleUserActiveStatusAsAdmin(_, { id, input }, ctx) {
+      if (input.isActive) {
+        return activateUser
+          .initialize({ User: ctx.connectors.User, ctx })
+          .execute({ id, input })
+      }
+      return deactivateUser
+        .initialize({ User: ctx.connectors.User, ctx })
+        .execute({ id, input })
+    },
+  },
+}
+
+module.exports = resolvers
diff --git a/packages/component-user/server/tests/activateUser.test.js b/packages/component-user/server/tests/activateUser.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..864b65f69fcb0aea2816002a6eb09c3a0cdea405
--- /dev/null
+++ b/packages/component-user/server/tests/activateUser.test.js
@@ -0,0 +1,28 @@
+const { cloneDeep } = require('lodash')
+const { Model, fixtures } = require('pubsweet-component-fixture-service')
+
+const { activateUser } = require('../use-cases')
+
+describe('activate use case', () => {
+  let testFixtures = {}
+  let models
+  const invalidPrefix = 'invalid***'
+
+  beforeEach(() => {
+    testFixtures = cloneDeep(fixtures)
+    models = Model.build(testFixtures)
+  })
+
+  it('activate an user', async () => {
+    const { user } = testFixtures.users
+    user.username = `${invalidPrefix}${user.username}`
+    user.isActive = false
+
+    const result = await activateUser
+      .initialize({ User: models.User })
+      .execute({ id: user.id, input: user })
+
+    expect(result.isActive).toBeTruthy()
+    expect(result.username.indexOf(invalidPrefix)).toBeLessThan(0)
+  })
+})
diff --git a/packages/component-user/server/tests/createUserAsAdmin.test.js b/packages/component-user/server/tests/createUserAsAdmin.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..80c3155a6d7fbbf00043cf714312d256e3146c97
--- /dev/null
+++ b/packages/component-user/server/tests/createUserAsAdmin.test.js
@@ -0,0 +1,64 @@
+const { cloneDeep } = require('lodash')
+const { Model, fixtures } = require('pubsweet-component-fixture-service')
+
+const { createUserAsAdmin } = require('../use-cases')
+
+class MockNotification {
+  constructor(user) {
+    this.user = user
+  }
+
+  async notifyUserAddedByAdmin(role) {
+    jest.fn(() => this)
+  }
+}
+
+describe('create user', () => {
+  let testFixtures = {}
+  let models
+
+  beforeEach(() => {
+    testFixtures = cloneDeep(fixtures)
+    models = Model.build(testFixtures)
+  })
+
+  it('edit an user as admin', async () => {
+    const input = {
+      email: 'alexandrescu@gmail.com',
+      username: 'alexandrescu@gmail.com',
+    }
+    const ctx = {
+      user: {
+        admin: true,
+      },
+    }
+
+    const result = await createUserAsAdmin
+      .initialize({ User: models.User, Notification: MockNotification, ctx })
+      .execute({
+        input,
+      })
+
+    expect(result).toMatchObject(input)
+  })
+
+  it('does not allow non admins to create user', async (...props) => {
+    const ctx = {
+      user: {
+        admin: false,
+      },
+    }
+
+    try {
+      await createUserAsAdmin
+        .initialize({
+          User: models.User,
+          Notification: MockNotification,
+          ctx,
+        })
+        .execute({})
+    } catch (e) {
+      expect(e.message).toBe('Unauthorized')
+    }
+  })
+})
diff --git a/packages/component-user/server/tests/deactivateUser.test.js b/packages/component-user/server/tests/deactivateUser.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..46a335ffa0bee01cb24f86ebb8c87351cf7b3b71
--- /dev/null
+++ b/packages/component-user/server/tests/deactivateUser.test.js
@@ -0,0 +1,28 @@
+const { cloneDeep } = require('lodash')
+const { Model, fixtures } = require('pubsweet-component-fixture-service')
+
+const { deactivateUser } = require('../use-cases')
+
+describe('deactivate use case', () => {
+  let testFixtures = {}
+  let models
+  const invalidPrefix = 'invalid***'
+
+  beforeEach(() => {
+    testFixtures = cloneDeep(fixtures)
+    models = Model.build(testFixtures)
+  })
+
+  it('deactivate an user', async () => {
+    const { user } = testFixtures.users
+    user.isActive = false
+
+    const result = await deactivateUser
+      .initialize({ User: models.User })
+      .execute({ id: user.id, input: user })
+
+    expect(result.isActive).toBeFalsy()
+    // invalid prefix should be present in the username
+    expect(result.username.indexOf(invalidPrefix)).toBeGreaterThanOrEqual(0)
+  })
+})
diff --git a/packages/component-user/server/tests/editUser.test.js b/packages/component-user/server/tests/editUser.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..baf29782f84c01334faaf59a732e978f8ed8fb25
--- /dev/null
+++ b/packages/component-user/server/tests/editUser.test.js
@@ -0,0 +1,30 @@
+const { cloneDeep } = require('lodash')
+const { Model, fixtures } = require('pubsweet-component-fixture-service')
+
+const { editUser } = require('../use-cases')
+
+describe('edit user as admin', () => {
+  let testFixtures = {}
+  let models
+
+  beforeEach(() => {
+    testFixtures = cloneDeep(fixtures)
+    models = Model.build(testFixtures)
+  })
+
+  it('edit an user', async () => {
+    const { user } = testFixtures.users
+    const input = {
+      ...user,
+      affiliation: 'TSD',
+      firstName: 'Sebibastian',
+    }
+
+    const result = await editUser.initialize({ User: models.User }).execute({
+      id: input.id,
+      input,
+    })
+
+    expect(result).toMatchObject(input)
+  })
+})
diff --git a/packages/component-user/typeDefs.js b/packages/component-user/server/typeDefs.js
similarity index 52%
rename from packages/component-user/typeDefs.js
rename to packages/component-user/server/typeDefs.js
index 3583a27be3d3a91e4f8aeb6de76bb57b59a1a001..249a380d2e775f1911c69fe9d95f75cc0279dffe 100644
--- a/packages/component-user/typeDefs.js
+++ b/packages/component-user/server/typeDefs.js
@@ -1,9 +1,4 @@
 module.exports = `
-  type Creasta {
-    id: String!
-    name: String
-  }
-
   extend type User {
     affiliation: String
     country: String
@@ -17,15 +12,30 @@ module.exports = `
   }
 
   extend input UserInput {
-    isActive: Boolean
-    isConfirmed: Boolean
     firstName: String
     lastName: String
     title: String
     country: String
     affiliation: String
-    admin: Boolean
-    editorInChief: Boolean
-    handlingEditor: Boolean
+    role: AllowedRole
+  }
+
+  input UserStatusInput {
+    email: String!
+    username: String!
+    isActive: Boolean
+  }
+
+  extend type Mutation {
+    addUserAsAdmin(input: UserInput!): User
+    editUserAsAdmin(id: ID!, input: UserInput!): User
+    toggleUserActiveStatusAsAdmin(id: ID!, input: UserStatusInput): User
+  }
+
+  enum AllowedRole {
+    editorInChief
+    handlingEditor
+    admin
+    author
   }
 `
diff --git a/packages/component-user/server/use-cases/activateUser.js b/packages/component-user/server/use-cases/activateUser.js
new file mode 100644
index 0000000000000000000000000000000000000000..7572f52fc44f815a837bcb671d8699803cd2e2c0
--- /dev/null
+++ b/packages/component-user/server/use-cases/activateUser.js
@@ -0,0 +1,6 @@
+module.exports.initialize = ({ User, ctx }) => ({
+  execute: async ({ id, input }) => {
+    const username = input.username.replace('invalid***', '')
+    return User.update(id, { ...input, username, isActive: true }, ctx)
+  },
+})
diff --git a/packages/component-user/server/use-cases/createUserAsAdmin.js b/packages/component-user/server/use-cases/createUserAsAdmin.js
new file mode 100644
index 0000000000000000000000000000000000000000..6a46215e4067744cd558983c6bd8aed3be95aacb
--- /dev/null
+++ b/packages/component-user/server/use-cases/createUserAsAdmin.js
@@ -0,0 +1,14 @@
+module.exports.initialize = ({ User, Notification, ctx }) => ({
+  execute: async ({ input }) => {
+    const { admin } = await User.fetchOne(ctx.user, ctx)
+    if (!admin) {
+      throw new Error('Unauthorized')
+    }
+
+    const user = await User.create(input, ctx)
+    const notification = new Notification(user)
+    await notification.notifyUserAddedByAdmin(input.role)
+
+    return user
+  },
+})
diff --git a/packages/component-user/server/use-cases/deactivateUser.js b/packages/component-user/server/use-cases/deactivateUser.js
new file mode 100644
index 0000000000000000000000000000000000000000..6c1703b638ea4bd75541a1c09465dd2e517cbd5b
--- /dev/null
+++ b/packages/component-user/server/use-cases/deactivateUser.js
@@ -0,0 +1,7 @@
+module.exports.initialize = ({ User, ctx }) => ({
+  execute: async ({ id, input }) => {
+    const username = `invalid***${input.username}`
+
+    return User.update(id, { ...input, username, isActive: false }, ctx)
+  },
+})
diff --git a/packages/component-user/server/use-cases/editUser.js b/packages/component-user/server/use-cases/editUser.js
new file mode 100644
index 0000000000000000000000000000000000000000..fff98ac5cebd104c24f899fa92bebd847c7c10c4
--- /dev/null
+++ b/packages/component-user/server/use-cases/editUser.js
@@ -0,0 +1,3 @@
+module.exports.initialize = ({ User, ctx }) => ({
+  execute: ({ id, input }) => User.update(id, input, ctx),
+})
diff --git a/packages/component-user/server/use-cases/index.js b/packages/component-user/server/use-cases/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..00ff37cad94784492bb38a8579aab8d664feff9c
--- /dev/null
+++ b/packages/component-user/server/use-cases/index.js
@@ -0,0 +1,11 @@
+const activateUser = require('./activateUser')
+const deactivateUser = require('./deactivateUser')
+const editUser = require('./editUser')
+const createUserAsAdmin = require('./createUserAsAdmin')
+
+module.exports = {
+  editUser,
+  activateUser,
+  deactivateUser,
+  createUserAsAdmin,
+}
diff --git a/packages/component-user/server/user.js b/packages/component-user/server/user.js
new file mode 100644
index 0000000000000000000000000000000000000000..83e107959aab56dc564e63a869843f55fa02642d
--- /dev/null
+++ b/packages/component-user/server/user.js
@@ -0,0 +1,34 @@
+const Chance = require('chance')
+const { omit } = require('lodash')
+
+const chance = new Chance()
+
+module.exports = {
+  parseUserFromAdmin: input => {
+    const roles = {
+      admin: false,
+      editorInChief: false,
+      handlingEditor: false,
+    }
+    if (input.role && input.role !== 'author') {
+      roles[input.role] = true
+    }
+
+    return {
+      ...omit(input, ['role']),
+      ...roles,
+      isActive: true,
+      isConfirmed: false,
+      notifications: {
+        email: {
+          system: true,
+          user: true,
+        },
+      },
+      accessTokens: {
+        passwordReset: chance.hash(),
+        unsubscribe: chance.hash(),
+      },
+    }
+  },
+}
diff --git a/packages/component-user/user.js b/packages/component-user/user.js
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/packages/components-faraday/src/components/Admin/index.js b/packages/components-faraday/src/components/Admin/index.js
deleted file mode 100644
index 0bb7e9bedd5aa30878c77cf7bea2da6a179a7d31..0000000000000000000000000000000000000000
--- a/packages/components-faraday/src/components/Admin/index.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export { default as withUsersGQL } from './withUsersGQL'
-
-export { default as AdminUsers } from './AdminUsers'
-export { default as AdminRoute } from './AdminRoute'
-export { default as OpenUserForm } from './OpenUserForm'
-export { default as AdminDashboard } from './AdminDashboard'
diff --git a/packages/components-faraday/src/components/Admin/utils.js b/packages/components-faraday/src/components/Admin/utils.js
deleted file mode 100644
index 14835de252aa01e7e57419f3f28edbb8eca00136..0000000000000000000000000000000000000000
--- a/packages/components-faraday/src/components/Admin/utils.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import { pick, omit, isBoolean, replace } from 'lodash'
-import { update } from 'pubsweet-client/src/helpers/api'
-
-const generatePasswordHash = () =>
-  Array.from({ length: 4 }, () =>
-    Math.random()
-      .toString(36)
-      .slice(4),
-  ).join('')
-
-export const setAdmin = values => {
-  const newValues = { ...values, isActive: true }
-  if (newValues.roles && newValues.roles.includes('admin')) {
-    newValues.admin = true
-  } else {
-    newValues.admin = false
-  }
-
-  return {
-    ...omit(newValues, ['role']),
-    username: newValues.email,
-    isConfirmed: false,
-    password: 'defaultpass',
-    editorInChief: newValues.role === 'editorInChief',
-    handlingEditor: newValues.role === 'handlingEditor',
-    notifications: {
-      email: {
-        system: true,
-        user: true,
-      },
-    },
-    accessTokens: {
-      passwordReset: generatePasswordHash(),
-      unsubscribe: generatePasswordHash(),
-    },
-  }
-}
-
-export const parseUpdateUser = values => {
-  const parsedValues = {
-    ...values,
-    editorInChief: values.editorInChief || false,
-    handlingEditor: values.handlingEditor || false,
-    admin: values.admin || false,
-  }
-  const valuesToSave = [
-    'admin',
-    'firstName',
-    'lastName',
-    'affiliation',
-    'title',
-    'roles',
-    'editorInChief',
-    'handlingEditor',
-    'isActive',
-    'username',
-    'country',
-    'accessTokens',
-  ]
-
-  return pick(parsedValues, valuesToSave)
-}
-
-const toggleUserStatus = user => {
-  const { isActive, username } = user
-  let newState = true
-  let newUsername = ''
-
-  if (!isBoolean(isActive) || isActive) {
-    newState = false
-    newUsername = `invalid***${username}`
-  } else {
-    newUsername = replace(username, 'invalid***', '')
-  }
-
-  return {
-    ...user,
-    isActive: newState,
-    username: newUsername,
-  }
-}
-
-export const updateUserStatus = user => {
-  const updatedUser = toggleUserStatus(user)
-  return update(`/users/${user.id}`, parseUpdateUser(updatedUser))
-}
diff --git a/packages/components-faraday/src/components/Admin/withUsersGQL.js b/packages/components-faraday/src/components/Admin/withUsersGQL.js
deleted file mode 100644
index 807a319249eb930c93cbbd45f41b4779ee69b658..0000000000000000000000000000000000000000
--- a/packages/components-faraday/src/components/Admin/withUsersGQL.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import gql from 'graphql-tag'
-import { graphql } from 'react-apollo'
-import { compose, withHandlers, withProps } from 'recompose'
-
-const userFragment = gql`
-  fragment userDetails on User {
-    id
-    admin
-    email
-    title
-    country
-    username
-    lastName
-    isActive
-    firstName
-    affiliation
-    isConfirmed
-    editorInChief
-    handlingEditor
-  }
-`
-
-const getUsersQuery = gql`
-  {
-    users {
-      ...userDetails
-    }
-  }
-  ${userFragment}
-`
-
-const addUserMutation = gql`
-  mutation addUser($user: UserInput) {
-    createUser(input: $user) {
-      ...userDetails
-    }
-  }
-  ${userFragment}
-`
-
-const updateUserMutation = gql`
-  mutation updateUser($id: ID, $input: UserInput) {
-    updateUser(id: $id, input: $input) {
-      ...userDetails
-    }
-  }
-  ${userFragment}
-`
-
-export default compose(
-  graphql(getUsersQuery),
-  graphql(addUserMutation, {
-    name: 'addUser',
-  }),
-  graphql(updateUserMutation, {
-    name: 'updateUser',
-  }),
-  withHandlers({
-    addUser: ({ addUser }) => (values, props) => {},
-    updateUser: ({ updateUser }) => (
-      { __typename, id, ...input },
-      { props: { hideModal } },
-    ) => {
-      updateUser({
-        variables: {
-          id,
-          input,
-        },
-      }).then(hideModal)
-    },
-  }),
-  withProps(({ data }) => ({
-    users: data.users,
-  })),
-)
diff --git a/packages/xpub-faraday/app/routes.js b/packages/xpub-faraday/app/routes.js
index 95a60cdedf33e26ba516608667d3c78959b63bcc..cbc1a4804a6816c416e6bd83deff04b4aa891be5 100644
--- a/packages/xpub-faraday/app/routes.js
+++ b/packages/xpub-faraday/app/routes.js
@@ -11,6 +11,12 @@ import {
   UserProfilePage,
   ChangePasswordPage,
 } from 'pubsweet-components-faraday/src/components'
+
+import {
+  AdminRoute,
+  AdminUsers,
+  AdminDashboard,
+} from 'pubsweet-component-user/app'
 import { ManuscriptPage } from 'pubsweet-component-manuscript/src/components'
 import DashboardPage from 'pubsweet-components-faraday/src/components/Dashboard'
 import LoginPage from 'pubsweet-components-faraday/src/components/Login/LoginPage'
@@ -22,11 +28,6 @@ import {
   EQSDecisionPage,
   EQADecisionPage,
 } from 'pubsweet-components-faraday/src/components/UIComponents/'
-import {
-  AdminUsers,
-  AdminRoute,
-  AdminDashboard,
-} from 'pubsweet-components-faraday/src/components/Admin'
 import {
   ConfirmAccount,
   ReviewerSignUp,
diff --git a/packages/xpub-faraday/config/authsome-mode.js b/packages/xpub-faraday/config/authsome-mode.js
index f739b073104cea996d271dc6190ca908e7d08421..9bba3c507b3fc83fc986a4d7680ab0ce25d80d2e 100644
--- a/packages/xpub-faraday/config/authsome-mode.js
+++ b/packages/xpub-faraday/config/authsome-mode.js
@@ -226,6 +226,14 @@ async function applyAuthenticatedUserPolicy(user, operation, object, context) {
     ) {
       return helpers.isOwner({ user, object: object.fragment })
     }
+
+    // allow user to authenticate itself
+    if (
+      get(object, 'type') === 'user' &&
+      get(object, 'id') === get(user, 'id')
+    ) {
+      return true
+    }
   }
 
   if (operation === 'PATCH') {
diff --git a/packages/xpub-faraday/config/components.json b/packages/xpub-faraday/config/components.json
index 3083694230c19476ca18ffaac0a8d84ed7bf67e5..044b6a295a9bdb2f85b1fd4ad051fe37a8db7882 100644
--- a/packages/xpub-faraday/config/components.json
+++ b/packages/xpub-faraday/config/components.json
@@ -10,5 +10,6 @@
   "pubsweet-component-email",
   "pubsweet-component-manuscript",
   "pubsweet-component-manuscript-manager",
-  "pubsweet-component-publons"
+  "pubsweet-component-publons",
+  "pubsweet-component-user"
 ]
diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js
index 13eed68fde3e75c0cea41cde6d77d128783590d3..dff2f0db73f57fbf0b8023dce9a71a4f17ce0602 100644
--- a/packages/xpub-faraday/config/default.js
+++ b/packages/xpub-faraday/config/default.js
@@ -1,11 +1,9 @@
 require('dotenv').config()
 const path = require('path')
-const get = require('lodash/get')
 const logger = require('winston')
 const components = require('./components.json')
 const journalConfig = require('../app/config/journal')
-
-const users = require('../../component-user')
+const { get } = require('lodash')
 
 const getDbConfig = () => {
   if (process.env.DATABASE) {
@@ -48,8 +46,6 @@ module.exports = {
     secret: 'SECRET',
     enableExperimentalGraphql: true,
     graphiql: true,
-    typeDefs: `${users.typeDefs}`,
-    // resolvers,
   },
   'pubsweet-client': {
     API_ENDPOINT: '/api',
@@ -151,4 +147,5 @@ module.exports = {
   passwordStrengthRegex: new RegExp(
     '^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&,.?;\'*><)([}{}":`~+=_-\\|/])(?=.{6,128})',
   ),
+  hostname: process.env.HOSTNAME || 'http://localhost:3000',
 }
diff --git a/packages/xpub-faraday/config/validations.js b/packages/xpub-faraday/config/validations.js
index 562ea5242cef0bcd0c3268496b72e70be656cf4a..d8e9c5bbdea33f58fb9c6d845740170a45cad97e 100644
--- a/packages/xpub-faraday/config/validations.js
+++ b/packages/xpub-faraday/config/validations.js
@@ -146,17 +146,17 @@ module.exports = {
     orcid: Joi.object(),
     name: Joi.string(),
     username: Joi.string(),
-    isConfirmed: Joi.boolean(),
+    title: Joi.string().allow(''),
+    agreeTC: Joi.boolean(),
+    isActive: Joi.boolean(),
     firstName: Joi.string().allow(''),
     lastName: Joi.string().allow(''),
     affiliation: Joi.string().allow(''),
+    isConfirmed: Joi.boolean(),
+    editorInChief: Joi.boolean(),
     country: Joi.string().allow(''),
-    title: Joi.string().allow(''),
     teams: Joi.array(),
-    editorInChief: Joi.boolean(),
     handlingEditor: Joi.boolean(),
-    agreeTC: Joi.boolean(),
-    isActive: Joi.boolean(),
     passwordHash: Joi.string(),
     notifications: Joi.object({
       email: Joi.object({
diff --git a/yarn.lock b/yarn.lock
index 0800a7fa664a6c4a705bd6b8d6c6648f94854b68..2eea57c1648738bddcff0b6825fe5f5c660b9ce1 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -278,6 +278,15 @@
     lodash "^4.17.4"
     styled-components "^4.1.1"
 
+"@pubsweet/ui-toolkit@^2.0.3":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@pubsweet/ui-toolkit/-/ui-toolkit-2.0.3.tgz#673be0f68bfe3fb8c0a43401a3a032a6cfde75f7"
+  integrity sha512-zINDKK+A6kXllcYr8+hBdBij8pxF3uOWFGmQjctLBVHzgE6DTV0mPj2iv2WKezO1nE1EbJD5EyXqF4/72gQqQg==
+  dependencies:
+    color "^3.0.0"
+    lodash "^4.17.4"
+    styled-components "^4.1.1"
+
 "@pubsweet/ui-toolkit@latest":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@pubsweet/ui-toolkit/-/ui-toolkit-1.0.0.tgz#df05b54e7bbfabcb10c7afc2991752e1087d2298"
@@ -337,6 +346,31 @@
     redux-form "^7.0.3"
     styled-components "^4.1.1"
 
+"@pubsweet/ui@^9.0.3":
+  version "9.0.3"
+  resolved "https://registry.yarnpkg.com/@pubsweet/ui/-/ui-9.0.3.tgz#aac4746d6febecd88935e9b2ab1c9d0581baab29"
+  integrity sha512-8mfvxzNwTZIM0lgzHniJEn/fiyW7W5LyJh2pAYpc+Su8AuCDAKOCpQjeJlgtYzpJuZMoYJrRxfTbuLIz3c7q1g==
+  dependencies:
+    "@pubsweet/ui-toolkit" "^2.0.3"
+    babel-jest "^21.2.0"
+    classnames "^2.2.5"
+    enzyme "^3.7.0"
+    enzyme-adapter-react-16 "^1.1.1"
+    invariant "^2.2.3"
+    lodash "^4.17.4"
+    moment "^2.22.1"
+    prop-types "^15.5.10"
+    react "^16.2.0"
+    react-dom "^16.2.0"
+    react-feather "^1.0.8"
+    react-redux "^5.0.2"
+    react-router-dom "^4.2.2"
+    react-tag-autocomplete "^5.5.0"
+    recompose "^0.26.0"
+    redux "^3.6.0"
+    redux-form "^7.0.3"
+    styled-components "^4.1.1"
+
 "@types/async@2.0.49":
   version "2.0.49"
   resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.49.tgz#92e33d13f74c895cb9a7f38ba97db8431ed14bc0"
@@ -1392,6 +1426,14 @@ babel-jest@^22.4.0:
     babel-plugin-istanbul "^4.1.5"
     babel-preset-jest "^22.2.0"
 
+babel-jest@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.6.0.tgz#a644232366557a2240a0c083da6b25786185a2f1"
+  integrity sha512-lqKGG6LYXYu+DQh/slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc/22ArwqXNJrf0QaOBjZB0360qZMwXqDYQHXaew==
+  dependencies:
+    babel-plugin-istanbul "^4.1.6"
+    babel-preset-jest "^23.2.0"
+
 babel-loader@^7.1.2:
   version "7.1.2"
   resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.2.tgz#f6cbe122710f1aa2af4d881c6d5b54358ca24126"
@@ -1424,6 +1466,16 @@ babel-plugin-istanbul@^4.0.0, babel-plugin-istanbul@^4.1.5:
     istanbul-lib-instrument "^1.7.5"
     test-exclude "^4.1.1"
 
+babel-plugin-istanbul@^4.1.6:
+  version "4.1.6"
+  resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45"
+  integrity sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==
+  dependencies:
+    babel-plugin-syntax-object-rest-spread "^6.13.0"
+    find-up "^2.1.0"
+    istanbul-lib-instrument "^1.10.1"
+    test-exclude "^4.2.1"
+
 babel-plugin-jest-hoist@^21.2.0:
   version "21.2.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-21.2.0.tgz#2cef637259bd4b628a6cace039de5fcd14dbb006"
@@ -1434,6 +1486,11 @@ babel-plugin-jest-hoist@^22.2.0:
   resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.2.0.tgz#bd34f39d652406669713b8c89e23ef25c890b993"
   integrity sha512-NwicD5n1YQaj6sM3PVULdPBDk1XdlWvh8xBeUJg3nqZwp79Vofb8Q7GOVeWoZZ/RMlMuJMMrEAgSQl/p392nLA==
 
+babel-plugin-jest-hoist@^23.2.0:
+  version "23.2.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz#e61fae05a1ca8801aadee57a6d66b8cefaf44167"
+  integrity sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc=
+
 "babel-plugin-styled-components@>= 1":
   version "1.9.2"
   resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.9.2.tgz#0e6a6587454dcb1c9a362a8fd31fc0b075ccd260"
@@ -1883,6 +1940,14 @@ babel-preset-jest@^22.2.0:
     babel-plugin-jest-hoist "^22.2.0"
     babel-plugin-syntax-object-rest-spread "^6.13.0"
 
+babel-preset-jest@^23.2.0:
+  version "23.2.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz#8ec7a03a138f001a1a8fb1e8113652bf1a55da46"
+  integrity sha1-jsegOhOPABoaj7HoETZSvxpV2kY=
+  dependencies:
+    babel-plugin-jest-hoist "^23.2.0"
+    babel-plugin-syntax-object-rest-spread "^6.13.0"
+
 babel-preset-react@^6.24.1:
   version "6.24.1"
   resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.24.1.tgz#ba69dfaea45fc3ec639b6a4ecea6e17702c91380"
@@ -1948,7 +2013,7 @@ babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
     babylon "^6.18.0"
     lodash "^4.17.4"
 
-babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
+babel-traverse@^6.0.0, babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
   integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=
@@ -1963,7 +2028,7 @@ babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
     invariant "^2.2.2"
     lodash "^4.17.4"
 
-babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
+babel-types@^6.0.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
   integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
@@ -2217,6 +2282,13 @@ browser-resolve@^1.11.2:
   dependencies:
     resolve "1.1.7"
 
+browser-resolve@^1.11.3:
+  version "1.11.3"
+  resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6"
+  integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==
+  dependencies:
+    resolve "1.1.7"
+
 browserify-aes@^1.0.0, browserify-aes@^1.0.4:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f"
@@ -2322,6 +2394,11 @@ buffer-equal-constant-time@1.0.1:
   resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
   integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
 
+buffer-from@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+  integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
 buffer-indexof@^1.0.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
@@ -4957,6 +5034,18 @@ expect@^22.4.0:
     jest-message-util "^22.4.0"
     jest-regex-util "^22.1.0"
 
+expect@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/expect/-/expect-23.6.0.tgz#1e0c8d3ba9a581c87bd71fb9bc8862d443425f98"
+  integrity sha512-dgSoOHgmtn/aDGRVFWclQyPDKl2CQRq0hmIEoUAuQs/2rn2NcvCWcSCovm6BLeuB/7EZuLGu2QfnR+qRt5OM4w==
+  dependencies:
+    ansi-styles "^3.2.0"
+    jest-diff "^23.6.0"
+    jest-get-type "^22.1.0"
+    jest-matcher-utils "^23.6.0"
+    jest-message-util "^23.4.0"
+    jest-regex-util "^23.3.0"
+
 express@^4.15.3, express@^4.16.1:
   version "4.16.2"
   resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
@@ -5516,6 +5605,21 @@ formik@^1.3.2:
     tslib "^1.9.3"
     warning "^3.0.0"
 
+formik@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/formik/-/formik-1.4.0.tgz#4261769f765dd41b7e791958fde7a08516d5920a"
+  integrity sha512-HOlb4cEgjTZ+5VMCYDlXt1r5Bt9wLhIH6uvJCAhJaIvqehmIM1RdzhYel8tCFPXzCcCx8QeZh3UcWKye5rsJmw==
+  dependencies:
+    create-react-context "^0.2.2"
+    deepmerge "^2.1.1"
+    hoist-non-react-statics "^2.5.5"
+    lodash "^4.17.11"
+    lodash-es "^4.17.11"
+    prop-types "^15.6.1"
+    react-fast-compare "^2.0.1"
+    tslib "^1.9.3"
+    warning "^3.0.0"
+
 forwarded@~0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@@ -7171,11 +7275,33 @@ istanbul-api@^1.1.14:
     mkdirp "^0.5.1"
     once "^1.4.0"
 
+istanbul-api@^1.3.1:
+  version "1.3.7"
+  resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.3.7.tgz#a86c770d2b03e11e3f778cd7aedd82d2722092aa"
+  integrity sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==
+  dependencies:
+    async "^2.1.4"
+    fileset "^2.0.2"
+    istanbul-lib-coverage "^1.2.1"
+    istanbul-lib-hook "^1.2.2"
+    istanbul-lib-instrument "^1.10.2"
+    istanbul-lib-report "^1.1.5"
+    istanbul-lib-source-maps "^1.2.6"
+    istanbul-reports "^1.5.1"
+    js-yaml "^3.7.0"
+    mkdirp "^0.5.1"
+    once "^1.4.0"
+
 istanbul-lib-coverage@^1.1.1, istanbul-lib-coverage@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.2.tgz#4113c8ff6b7a40a1ef7350b01016331f63afde14"
   integrity sha512-tZYA0v5A7qBSsOzcebJJ/z3lk3oSzH62puG78DbBA1+zupipX2CakDyiPV3pOb8He+jBwVimuwB0dTnh38hX0w==
 
+istanbul-lib-coverage@^1.2.0, istanbul-lib-coverage@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0"
+  integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==
+
 istanbul-lib-hook@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz#8538d970372cb3716d53e55523dd54b557a8d89b"
@@ -7183,6 +7309,26 @@ istanbul-lib-hook@^1.1.0:
   dependencies:
     append-transform "^0.4.0"
 
+istanbul-lib-hook@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz#bc6bf07f12a641fbf1c85391d0daa8f0aea6bf86"
+  integrity sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==
+  dependencies:
+    append-transform "^0.4.0"
+
+istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.10.2:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz#1f55ed10ac3c47f2bdddd5307935126754d0a9ca"
+  integrity sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==
+  dependencies:
+    babel-generator "^6.18.0"
+    babel-template "^6.16.0"
+    babel-traverse "^6.18.0"
+    babel-types "^6.18.0"
+    babylon "^6.18.0"
+    istanbul-lib-coverage "^1.2.1"
+    semver "^5.3.0"
+
 istanbul-lib-instrument@^1.7.5, istanbul-lib-instrument@^1.8.0, istanbul-lib-instrument@^1.9.2:
   version "1.9.2"
   resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.2.tgz#84905bf47f7e0b401d6b840da7bad67086b4aab6"
@@ -7206,6 +7352,16 @@ istanbul-lib-report@^1.1.3:
     path-parse "^1.0.5"
     supports-color "^3.1.2"
 
+istanbul-lib-report@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz#f2a657fc6282f96170aaf281eb30a458f7f4170c"
+  integrity sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==
+  dependencies:
+    istanbul-lib-coverage "^1.2.1"
+    mkdirp "^0.5.1"
+    path-parse "^1.0.5"
+    supports-color "^3.1.2"
+
 istanbul-lib-source-maps@^1.2.1, istanbul-lib-source-maps@^1.2.3:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz#20fb54b14e14b3fb6edb6aca3571fd2143db44e6"
@@ -7217,6 +7373,17 @@ istanbul-lib-source-maps@^1.2.1, istanbul-lib-source-maps@^1.2.3:
     rimraf "^2.6.1"
     source-map "^0.5.3"
 
+istanbul-lib-source-maps@^1.2.4, istanbul-lib-source-maps@^1.2.6:
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz#37b9ff661580f8fca11232752ee42e08c6675d8f"
+  integrity sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==
+  dependencies:
+    debug "^3.1.0"
+    istanbul-lib-coverage "^1.2.1"
+    mkdirp "^0.5.1"
+    rimraf "^2.6.1"
+    source-map "^0.5.3"
+
 istanbul-reports@^1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.4.tgz#5ccba5e22b7b5a5d91d5e0a830f89be334bf97bd"
@@ -7224,6 +7391,13 @@ istanbul-reports@^1.1.4:
   dependencies:
     handlebars "^4.0.3"
 
+istanbul-reports@^1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.1.tgz#97e4dbf3b515e8c484caea15d6524eebd3ff4e1a"
+  integrity sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==
+  dependencies:
+    handlebars "^4.0.3"
+
 items@2.x.x:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/items/-/items-2.1.1.tgz#8bd16d9c83b19529de5aea321acaada78364a198"
@@ -7251,6 +7425,13 @@ jest-changed-files@^22.2.0:
   dependencies:
     throat "^4.0.0"
 
+jest-changed-files@^23.4.2:
+  version "23.4.2"
+  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.4.2.tgz#1eed688370cd5eebafe4ae93d34bb3b64968fe83"
+  integrity sha512-EyNhTAUWEfwnK0Is/09LxoqNDOn7mU7S3EHskG52djOFS/z+IT0jT3h3Ql61+dklcG7bJJitIWEMB4Sp1piHmA==
+  dependencies:
+    throat "^4.0.0"
+
 jest-cli@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-22.4.0.tgz#234d6175166e87ecab40c6e5a4f7b3f6a4cd4257"
@@ -7291,6 +7472,48 @@ jest-cli@^22.4.0:
     which "^1.2.12"
     yargs "^10.0.3"
 
+jest-cli@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-23.6.0.tgz#61ab917744338f443ef2baa282ddffdd658a5da4"
+  integrity sha512-hgeD1zRUp1E1zsiyOXjEn4LzRLWdJBV//ukAHGlx6s5mfCNJTbhbHjgxnDUXA8fsKWN/HqFFF6X5XcCwC/IvYQ==
+  dependencies:
+    ansi-escapes "^3.0.0"
+    chalk "^2.0.1"
+    exit "^0.1.2"
+    glob "^7.1.2"
+    graceful-fs "^4.1.11"
+    import-local "^1.0.0"
+    is-ci "^1.0.10"
+    istanbul-api "^1.3.1"
+    istanbul-lib-coverage "^1.2.0"
+    istanbul-lib-instrument "^1.10.1"
+    istanbul-lib-source-maps "^1.2.4"
+    jest-changed-files "^23.4.2"
+    jest-config "^23.6.0"
+    jest-environment-jsdom "^23.4.0"
+    jest-get-type "^22.1.0"
+    jest-haste-map "^23.6.0"
+    jest-message-util "^23.4.0"
+    jest-regex-util "^23.3.0"
+    jest-resolve-dependencies "^23.6.0"
+    jest-runner "^23.6.0"
+    jest-runtime "^23.6.0"
+    jest-snapshot "^23.6.0"
+    jest-util "^23.4.0"
+    jest-validate "^23.6.0"
+    jest-watcher "^23.4.0"
+    jest-worker "^23.2.0"
+    micromatch "^2.3.11"
+    node-notifier "^5.2.1"
+    prompts "^0.1.9"
+    realpath-native "^1.0.0"
+    rimraf "^2.5.4"
+    slash "^1.0.0"
+    string-length "^2.0.0"
+    strip-ansi "^4.0.0"
+    which "^1.2.12"
+    yargs "^11.0.0"
+
 jest-config@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-22.4.0.tgz#34ab50ff52e68a3b0f2dd5df91bfd9b8cf2aa474"
@@ -7308,6 +7531,26 @@ jest-config@^22.4.0:
     jest-validate "^22.4.0"
     pretty-format "^22.4.0"
 
+jest-config@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.6.0.tgz#f82546a90ade2d8c7026fbf6ac5207fc22f8eb1d"
+  integrity sha512-i8V7z9BeDXab1+VNo78WM0AtWpBRXJLnkT+lyT+Slx/cbP5sZJ0+NDuLcmBE5hXAoK0aUp7vI+MOxR+R4d8SRQ==
+  dependencies:
+    babel-core "^6.0.0"
+    babel-jest "^23.6.0"
+    chalk "^2.0.1"
+    glob "^7.1.1"
+    jest-environment-jsdom "^23.4.0"
+    jest-environment-node "^23.4.0"
+    jest-get-type "^22.1.0"
+    jest-jasmine2 "^23.6.0"
+    jest-regex-util "^23.3.0"
+    jest-resolve "^23.6.0"
+    jest-util "^23.4.0"
+    jest-validate "^23.6.0"
+    micromatch "^2.3.11"
+    pretty-format "^23.6.0"
+
 jest-diff@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-22.4.0.tgz#384c2b78519ca44ca126382df53f134289232525"
@@ -7318,6 +7561,16 @@ jest-diff@^22.4.0:
     jest-get-type "^22.1.0"
     pretty-format "^22.4.0"
 
+jest-diff@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.6.0.tgz#1500f3f16e850bb3d71233408089be099f610c7d"
+  integrity sha512-Gz9l5Ov+X3aL5L37IT+8hoCUsof1CVYBb2QEkOupK64XyRR3h+uRpYIm97K7sY8diFxowR8pIGEdyfMKTixo3g==
+  dependencies:
+    chalk "^2.0.1"
+    diff "^3.2.0"
+    jest-get-type "^22.1.0"
+    pretty-format "^23.6.0"
+
 jest-docblock@^21.0.0:
   version "21.2.0"
   resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414"
@@ -7330,6 +7583,21 @@ jest-docblock@^22.4.0:
   dependencies:
     detect-newline "^2.1.0"
 
+jest-docblock@^23.2.0:
+  version "23.2.0"
+  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-23.2.0.tgz#f085e1f18548d99fdd69b20207e6fd55d91383a7"
+  integrity sha1-8IXh8YVI2Z/dabICB+b9VdkTg6c=
+  dependencies:
+    detect-newline "^2.1.0"
+
+jest-each@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.6.0.tgz#ba0c3a82a8054387016139c733a05242d3d71575"
+  integrity sha512-x7V6M/WGJo6/kLoissORuvLIeAoyo2YqLOoCDkohgJ4XOXSqOtyvr8FbInlAWS77ojBsZrafbozWoKVRdtxFCg==
+  dependencies:
+    chalk "^2.0.1"
+    pretty-format "^23.6.0"
+
 jest-environment-jsdom@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.4.0.tgz#09df84a1faf1ca47096aafc89411a095378f628e"
@@ -7339,6 +7607,15 @@ jest-environment-jsdom@^22.4.0:
     jest-util "^22.4.0"
     jsdom "^11.5.1"
 
+jest-environment-jsdom@^23.4.0:
+  version "23.4.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-23.4.0.tgz#056a7952b3fea513ac62a140a2c368c79d9e6023"
+  integrity sha1-BWp5UrP+pROsYqFAosNox52eYCM=
+  dependencies:
+    jest-mock "^23.2.0"
+    jest-util "^23.4.0"
+    jsdom "^11.5.1"
+
 jest-environment-node@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.4.0.tgz#b6d9458275053028d4b1658851c3475ab22dfb56"
@@ -7347,6 +7624,14 @@ jest-environment-node@^22.4.0:
     jest-mock "^22.2.0"
     jest-util "^22.4.0"
 
+jest-environment-node@^23.4.0:
+  version "23.4.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-23.4.0.tgz#57e80ed0841dea303167cce8cd79521debafde10"
+  integrity sha1-V+gO0IQd6jAxZ8zozXlSHeuv3hA=
+  dependencies:
+    jest-mock "^23.2.0"
+    jest-util "^23.4.0"
+
 jest-get-type@^21.2.0:
   version "21.2.0"
   resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23"
@@ -7370,6 +7655,20 @@ jest-haste-map@^22.4.0:
     micromatch "^2.3.11"
     sane "^2.0.0"
 
+jest-haste-map@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.6.0.tgz#2e3eb997814ca696d62afdb3f2529f5bbc935e16"
+  integrity sha512-uyNhMyl6dr6HaXGHp8VF7cK6KpC6G9z9LiMNsst+rJIZ8l7wY0tk8qwjPmEghczojZ2/ZhtEdIabZ0OQRJSGGg==
+  dependencies:
+    fb-watchman "^2.0.0"
+    graceful-fs "^4.1.11"
+    invariant "^2.2.4"
+    jest-docblock "^23.2.0"
+    jest-serializer "^23.0.1"
+    jest-worker "^23.2.0"
+    micromatch "^2.3.11"
+    sane "^2.0.0"
+
 jest-jasmine2@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.4.0.tgz#1d9b607ede12a600ecadda2c8d89918d7d3c4d26"
@@ -7387,6 +7686,24 @@ jest-jasmine2@^22.4.0:
     jest-snapshot "^22.4.0"
     source-map-support "^0.5.0"
 
+jest-jasmine2@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.6.0.tgz#840e937f848a6c8638df24360ab869cc718592e0"
+  integrity sha512-pe2Ytgs1nyCs8IvsEJRiRTPC0eVYd8L/dXJGU08GFuBwZ4sYH/lmFDdOL3ZmvJR8QKqV9MFuwlsAi/EWkFUbsQ==
+  dependencies:
+    babel-traverse "^6.0.0"
+    chalk "^2.0.1"
+    co "^4.6.0"
+    expect "^23.6.0"
+    is-generator-fn "^1.0.0"
+    jest-diff "^23.6.0"
+    jest-each "^23.6.0"
+    jest-matcher-utils "^23.6.0"
+    jest-message-util "^23.4.0"
+    jest-snapshot "^23.6.0"
+    jest-util "^23.4.0"
+    pretty-format "^23.6.0"
+
 jest-leak-detector@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-22.4.0.tgz#64da77f05b001c96d2062226e079f89989c4aa2f"
@@ -7394,6 +7711,13 @@ jest-leak-detector@^22.4.0:
   dependencies:
     pretty-format "^22.4.0"
 
+jest-leak-detector@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-23.6.0.tgz#e4230fd42cf381a1a1971237ad56897de7e171de"
+  integrity sha512-f/8zA04rsl1Nzj10HIyEsXvYlMpMPcy0QkQilVZDFOaPbv2ur71X5u2+C4ZQJGyV/xvVXtCCZ3wQ99IgQxftCg==
+  dependencies:
+    pretty-format "^23.6.0"
+
 jest-matcher-utils@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-22.4.0.tgz#d55f5faf2270462736bdf7c7485ee931c9d4b6a1"
@@ -7403,6 +7727,15 @@ jest-matcher-utils@^22.4.0:
     jest-get-type "^22.1.0"
     pretty-format "^22.4.0"
 
+jest-matcher-utils@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz#726bcea0c5294261a7417afb6da3186b4b8cac80"
+  integrity sha512-rosyCHQfBcol4NsckTn01cdelzWLU9Cq7aaigDf8VwwpIRvWE/9zLgX2bON+FkEW69/0UuYslUe22SOdEf2nog==
+  dependencies:
+    chalk "^2.0.1"
+    jest-get-type "^22.1.0"
+    pretty-format "^23.6.0"
+
 jest-message-util@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-22.4.0.tgz#e3d861df16d2fee60cb2bc8feac2188a42579642"
@@ -7414,16 +7747,37 @@ jest-message-util@^22.4.0:
     slash "^1.0.0"
     stack-utils "^1.0.1"
 
+jest-message-util@^23.4.0:
+  version "23.4.0"
+  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.4.0.tgz#17610c50942349508d01a3d1e0bda2c079086a9f"
+  integrity sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=
+  dependencies:
+    "@babel/code-frame" "^7.0.0-beta.35"
+    chalk "^2.0.1"
+    micromatch "^2.3.11"
+    slash "^1.0.0"
+    stack-utils "^1.0.1"
+
 jest-mock@^22.2.0:
   version "22.2.0"
   resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-22.2.0.tgz#444b3f9488a7473adae09bc8a77294afded397a7"
   integrity sha512-eOfoUYLOB/JlxChOFkh/bzpWGqUXb9I+oOpkprHHs9L7nUNfL8Rk28h1ycWrqzWCEQ/jZBg/xIv7VdQkfAkOhw==
 
+jest-mock@^23.2.0:
+  version "23.2.0"
+  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-23.2.0.tgz#ad1c60f29e8719d47c26e1138098b6d18b261134"
+  integrity sha1-rRxg8p6HGdR8JuETgJi20YsmETQ=
+
 jest-regex-util@^22.1.0:
   version "22.1.0"
   resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-22.1.0.tgz#5daf2fe270074b6da63e5d85f1c9acc866768f53"
   integrity sha512-on0LqVS6Xeh69sw3d1RukVnur+lVOl3zkmb0Q54FHj9wHoq6dbtWqb3TSlnVUyx36hqjJhjgs/QLqs07Bzu72Q==
 
+jest-regex-util@^23.3.0:
+  version "23.3.0"
+  resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-23.3.0.tgz#5f86729547c2785c4002ceaa8f849fe8ca471bc5"
+  integrity sha1-X4ZylUfCeFxAAs6qj4Sf6MpHG8U=
+
 jest-resolve-dependencies@^22.1.0:
   version "22.1.0"
   resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-22.1.0.tgz#340e4139fb13315cd43abc054e6c06136be51e31"
@@ -7431,6 +7785,14 @@ jest-resolve-dependencies@^22.1.0:
   dependencies:
     jest-regex-util "^22.1.0"
 
+jest-resolve-dependencies@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.6.0.tgz#b4526af24c8540d9a3fab102c15081cf509b723d"
+  integrity sha512-EkQWkFWjGKwRtRyIwRwI6rtPAEyPWlUC2MpzHissYnzJeHcyCn1Hc8j7Nn1xUVrS5C6W5+ZL37XTem4D4pLZdA==
+  dependencies:
+    jest-regex-util "^23.3.0"
+    jest-snapshot "^23.6.0"
+
 jest-resolve@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.4.0.tgz#c3550280d77c47c2885809e7dc8e42560f0b3e71"
@@ -7439,6 +7801,15 @@ jest-resolve@^22.4.0:
     browser-resolve "^1.11.2"
     chalk "^2.0.1"
 
+jest-resolve@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.6.0.tgz#cf1d1a24ce7ee7b23d661c33ba2150f3aebfa0ae"
+  integrity sha512-XyoRxNtO7YGpQDmtQCmZjum1MljDqUCob7XlZ6jy9gsMugHdN2hY4+Acz9Qvjz2mSsOnPSH7skBmDYCHXVZqkA==
+  dependencies:
+    browser-resolve "^1.11.3"
+    chalk "^2.0.1"
+    realpath-native "^1.0.0"
+
 jest-runner@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-22.4.0.tgz#2509b82834ab4aa7e984ff464626397c556177a7"
@@ -7456,6 +7827,25 @@ jest-runner@^22.4.0:
     jest-worker "^22.2.2"
     throat "^4.0.0"
 
+jest-runner@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.6.0.tgz#3894bd219ffc3f3cb94dc48a4170a2e6f23a5a38"
+  integrity sha512-kw0+uj710dzSJKU6ygri851CObtCD9cN8aNkg8jWJf4ewFyEa6kwmiH/r/M1Ec5IL/6VFa0wnAk6w+gzUtjJzA==
+  dependencies:
+    exit "^0.1.2"
+    graceful-fs "^4.1.11"
+    jest-config "^23.6.0"
+    jest-docblock "^23.2.0"
+    jest-haste-map "^23.6.0"
+    jest-jasmine2 "^23.6.0"
+    jest-leak-detector "^23.6.0"
+    jest-message-util "^23.4.0"
+    jest-runtime "^23.6.0"
+    jest-util "^23.4.0"
+    jest-worker "^23.2.0"
+    source-map-support "^0.5.6"
+    throat "^4.0.0"
+
 jest-runtime@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-22.4.0.tgz#a4e3e3709a2289725790ed51ca41526a5700ddc4"
@@ -7482,11 +7872,43 @@ jest-runtime@^22.4.0:
     write-file-atomic "^2.1.0"
     yargs "^10.0.3"
 
+jest-runtime@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-23.6.0.tgz#059e58c8ab445917cd0e0d84ac2ba68de8f23082"
+  integrity sha512-ycnLTNPT2Gv+TRhnAYAQ0B3SryEXhhRj1kA6hBPSeZaNQkJ7GbZsxOLUkwg6YmvWGdX3BB3PYKFLDQCAE1zNOw==
+  dependencies:
+    babel-core "^6.0.0"
+    babel-plugin-istanbul "^4.1.6"
+    chalk "^2.0.1"
+    convert-source-map "^1.4.0"
+    exit "^0.1.2"
+    fast-json-stable-stringify "^2.0.0"
+    graceful-fs "^4.1.11"
+    jest-config "^23.6.0"
+    jest-haste-map "^23.6.0"
+    jest-message-util "^23.4.0"
+    jest-regex-util "^23.3.0"
+    jest-resolve "^23.6.0"
+    jest-snapshot "^23.6.0"
+    jest-util "^23.4.0"
+    jest-validate "^23.6.0"
+    micromatch "^2.3.11"
+    realpath-native "^1.0.0"
+    slash "^1.0.0"
+    strip-bom "3.0.0"
+    write-file-atomic "^2.1.0"
+    yargs "^11.0.0"
+
 jest-serializer@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-22.4.0.tgz#b5d145b98c4b0d2c20ab686609adbb81fe23b566"
   integrity sha512-dnqde95MiYfdc1ZJpjEiHCRvRGGJHPsZQARJFucEGIaOzxqqS9/tt2WzD/OUSGT6kxaEGLQE92faVJGdoCu+Rw==
 
+jest-serializer@^23.0.1:
+  version "23.0.1"
+  resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-23.0.1.tgz#a3776aeb311e90fe83fab9e533e85102bd164165"
+  integrity sha1-o3dq6zEekP6D+rnlM+hRAr0WQWU=
+
 jest-snapshot@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-22.4.0.tgz#03d3ce63f8fa7352388afc6a3c8b5ccc3a180ed7"
@@ -7499,6 +7921,22 @@ jest-snapshot@^22.4.0:
     natural-compare "^1.4.0"
     pretty-format "^22.4.0"
 
+jest-snapshot@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.6.0.tgz#f9c2625d1b18acda01ec2d2b826c0ce58a5aa17a"
+  integrity sha512-tM7/Bprftun6Cvj2Awh/ikS7zV3pVwjRYU2qNYS51VZHgaAMBs5l4o/69AiDHhQrj5+LA2Lq4VIvK7zYk/bswg==
+  dependencies:
+    babel-types "^6.0.0"
+    chalk "^2.0.1"
+    jest-diff "^23.6.0"
+    jest-matcher-utils "^23.6.0"
+    jest-message-util "^23.4.0"
+    jest-resolve "^23.6.0"
+    mkdirp "^0.5.1"
+    natural-compare "^1.4.0"
+    pretty-format "^23.6.0"
+    semver "^5.5.0"
+
 jest-util@^22.4.0:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-22.4.0.tgz#ebdc147548d613c5faf7c7534051f59740c98ada"
@@ -7511,6 +7949,20 @@ jest-util@^22.4.0:
     jest-message-util "^22.4.0"
     mkdirp "^0.5.1"
 
+jest-util@^23.4.0:
+  version "23.4.0"
+  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-23.4.0.tgz#4d063cb927baf0a23831ff61bec2cbbf49793561"
+  integrity sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE=
+  dependencies:
+    callsites "^2.0.0"
+    chalk "^2.0.1"
+    graceful-fs "^4.1.11"
+    is-ci "^1.0.10"
+    jest-message-util "^23.4.0"
+    mkdirp "^0.5.1"
+    slash "^1.0.0"
+    source-map "^0.6.0"
+
 jest-validate@^21.1.0:
   version "21.2.1"
   resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7"
@@ -7532,6 +7984,25 @@ jest-validate@^22.4.0:
     leven "^2.1.0"
     pretty-format "^22.4.0"
 
+jest-validate@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.6.0.tgz#36761f99d1ed33fcd425b4e4c5595d62b6597474"
+  integrity sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A==
+  dependencies:
+    chalk "^2.0.1"
+    jest-get-type "^22.1.0"
+    leven "^2.1.0"
+    pretty-format "^23.6.0"
+
+jest-watcher@^23.4.0:
+  version "23.4.0"
+  resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.4.0.tgz#d2e28ce74f8dad6c6afc922b92cabef6ed05c91c"
+  integrity sha1-0uKM50+NrWxq/JIrksq+9u0FyRw=
+  dependencies:
+    ansi-escapes "^3.0.0"
+    chalk "^2.0.1"
+    string-length "^2.0.0"
+
 jest-worker@^22.2.2:
   version "22.2.2"
   resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-22.2.2.tgz#c1f5dc39976884b81f68ec50cb8532b2cbab3390"
@@ -7539,6 +8010,13 @@ jest-worker@^22.2.2:
   dependencies:
     merge-stream "^1.0.1"
 
+jest-worker@^23.2.0:
+  version "23.2.0"
+  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-23.2.0.tgz#faf706a8da36fae60eb26957257fa7b5d8ea02b9"
+  integrity sha1-+vcGqNo2+uYOsmlXJX+ntdjqArk=
+  dependencies:
+    merge-stream "^1.0.1"
+
 jest@^22.1.1:
   version "22.4.0"
   resolved "https://registry.yarnpkg.com/jest/-/jest-22.4.0.tgz#476e2c08c6c2a6dbb5cfec520b8bf1cd4c99afd7"
@@ -7547,6 +8025,14 @@ jest@^22.1.1:
     import-local "^1.0.0"
     jest-cli "^22.4.0"
 
+jest@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/jest/-/jest-23.6.0.tgz#ad5835e923ebf6e19e7a1d7529a432edfee7813d"
+  integrity sha512-lWzcd+HSiqeuxyhG+EnZds6iO3Y3ZEnMrfZq/OTGvF/C+Z4fPMCdhWTGSAiO2Oym9rbEXfwddHhh6jqrTF3+Lw==
+  dependencies:
+    import-local "^1.0.0"
+    jest-cli "^23.6.0"
+
 jmespath@0.15.0:
   version "0.15.0"
   resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217"
@@ -8240,6 +8726,11 @@ locate-path@^2.0.0:
     p-locate "^2.0.0"
     path-exists "^3.0.0"
 
+lodash-es@^4.17.11:
+  version "4.17.11"
+  resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0"
+  integrity sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==
+
 lodash-es@^4.17.3, lodash-es@^4.17.5, lodash-es@^4.2.1:
   version "4.17.5"
   resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.5.tgz#9fc6e737b1c4d151d8f9cae2247305d552ce748f"
@@ -10568,6 +11059,14 @@ pretty-format@^22.4.0:
     ansi-regex "^3.0.0"
     ansi-styles "^3.2.0"
 
+pretty-format@^23.6.0:
+  version "23.6.0"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760"
+  integrity sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==
+  dependencies:
+    ansi-regex "^3.0.0"
+    ansi-styles "^3.2.0"
+
 private@^0.1.6, private@^0.1.7, private@~0.1.5:
   version "0.1.8"
   resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@@ -10634,6 +11133,14 @@ prompt@^1.0.0, prompt@flatiron/prompt#1c95d1d8d333b5fbc13fa5f0619f3dcf0d514f87:
     utile "0.3.x"
     winston "2.x"
 
+prompts@^0.1.9:
+  version "0.1.14"
+  resolved "https://registry.yarnpkg.com/prompts/-/prompts-0.1.14.tgz#a8e15c612c5c9ec8f8111847df3337c9cbd443b2"
+  integrity sha512-rxkyiE9YH6zAz/rZpywySLKkpaj0NMVyNw1qhsubdbjjSgcayjTShDreZGlFMcGSu5sab3bAKPfFk78PB90+8w==
+  dependencies:
+    kleur "^2.0.1"
+    sisteransi "^0.1.1"
+
 prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0:
   version "15.6.0"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
@@ -11246,6 +11753,11 @@ react-fast-compare@^1.0.0:
   resolved "http://registry.npmjs.org/react-fast-compare/-/react-fast-compare-1.0.0.tgz#813a039155e49b43ceffe99528fe5e9d97a6c938"
   integrity sha512-dcQpdWr62flXQJuM8/bVEY5/10ad2SYBUafp8H4q4WHR3fTA/MMlp8mpzX12I0CCoEJc1P6QdiMg7U+7lFS6Rw==
 
+react-fast-compare@^2.0.1:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
+  integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
+
 react-feather@^1.0.8:
   version "1.0.8"
   resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-1.0.8.tgz#69b13d5c729949f194d33201dee91bab67fa31a2"
@@ -12411,6 +12923,11 @@ semver@5.3.0, semver@~5.3.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
   integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
 
+semver@^5.5.0:
+  version "5.6.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
+  integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
+
 send@0.16.1:
   version "0.16.1"
   resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3"
@@ -12593,6 +13110,11 @@ simple-swizzle@^0.2.2:
   dependencies:
     is-arrayish "^0.3.1"
 
+sisteransi@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce"
+  integrity sha512-PmGOd02bM9YO5ifxpw36nrNMBTptEtfRl4qUYl9SndkolplkrZZOW7PGHjrZL53QvMVj9nQ+TKqUnRsw4tJa4g==
+
 slash@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
@@ -12718,6 +13240,14 @@ source-map-support@^0.5.0, source-map-support@^0.5.1:
   dependencies:
     source-map "^0.6.0"
 
+source-map-support@^0.5.6:
+  version "0.5.9"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f"
+  integrity sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
 source-map-url@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
@@ -13487,6 +14017,17 @@ test-exclude@^4.1.1:
     read-pkg-up "^1.0.1"
     require-main-filename "^1.0.1"
 
+test-exclude@^4.2.1:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.3.tgz#a9a5e64474e4398339245a0a769ad7c2f4a97c20"
+  integrity sha512-SYbXgY64PT+4GAL2ocI3HwPa4Q4TBKm0cwAVeKOt/Aoc0gSpNRjJX8w0pA1LMKZ3LBmd8pYBqApFNQLII9kavA==
+  dependencies:
+    arrify "^1.0.1"
+    micromatch "^2.3.11"
+    object-assign "^4.1.0"
+    read-pkg-up "^1.0.1"
+    require-main-filename "^1.0.1"
+
 text-extensions@^1.0.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.7.0.tgz#faaaba2625ed746d568a23e4d0aacd9bf08a8b39"
@@ -14726,6 +15267,13 @@ xpub-validators@^0.0.5:
   dependencies:
     striptags "^3.1.0"
 
+xpub-validators@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/xpub-validators/-/xpub-validators-0.0.6.tgz#42ebc2d722b9ec6801cc44b7177eaa71029c1514"
+  integrity sha512-zkayEdxC3NQERycoTu8YUUPNlBuN153ESbIax9exmgrO0yt4LG3be+FirXcbkgIPYmgPAB0nnhPofOCTMTJx1g==
+  dependencies:
+    striptags "^3.1.0"
+
 xregexp@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
@@ -14784,6 +15332,13 @@ yargs-parser@^8.1.0:
   dependencies:
     camelcase "^4.1.0"
 
+yargs-parser@^9.0.2:
+  version "9.0.2"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077"
+  integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=
+  dependencies:
+    camelcase "^4.1.0"
+
 yargs@6.6.0:
   version "6.6.0"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
@@ -14821,6 +15376,24 @@ yargs@^10.0.3:
     y18n "^3.2.1"
     yargs-parser "^8.1.0"
 
+yargs@^11.0.0:
+  version "11.1.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77"
+  integrity sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==
+  dependencies:
+    cliui "^4.0.0"
+    decamelize "^1.1.1"
+    find-up "^2.1.0"
+    get-caller-file "^1.0.1"
+    os-locale "^2.0.0"
+    require-directory "^2.1.1"
+    require-main-filename "^1.0.1"
+    set-blocking "^2.0.0"
+    string-width "^2.0.0"
+    which-module "^2.0.0"
+    y18n "^3.2.1"
+    yargs-parser "^9.0.2"
+
 yargs@^7.0.0:
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"