diff --git a/packages/component-invite/src/helpers/helpers.js b/packages/component-invite/src/helpers/helpers.js
index bf0077c7692b634c51ab168063b5b2f16e4ee3fa..1376dc9945afcf01c65692b1ae80b7ea3755f151 100644
--- a/packages/component-invite/src/helpers/helpers.js
+++ b/packages/component-invite/src/helpers/helpers.js
@@ -44,7 +44,7 @@ const validateEmailAndToken = async (email, token, userModel) => {
         message: e.details[0].message,
       }
     }
-    logger.error(e)
+    logger.error('internal server error')
     return {
       success: false,
       status: 500,
diff --git a/packages/component-invite/src/routes/post.js b/packages/component-invite/src/routes/post.js
index 52d366519669c91b566d5f8da5f96eb42c8817a2..b70c27364eca635f3e2e8dbe9efc5de0a0c4f4aa 100644
--- a/packages/component-invite/src/routes/post.js
+++ b/packages/component-invite/src/routes/post.js
@@ -32,13 +32,13 @@ module.exports = models => async (req, res) => {
       res.status(inviteRight.status).json({
         error: inviteRight.message,
       })
-      logger.error(`incorrect role when inviting a user`)
+      logger.error(`incorrect role when inviting a ${role}`)
       return
     }
   } else if (collectionId) {
     if (!configRoles.collection.includes(role)) {
       res
-        .status(400)
+        .status(403)
         .json({ error: `Role ${role} cannot be set on collections` })
       logger.error(`invitation has been attempted with invalid role: ${role}`)
       return
@@ -65,7 +65,7 @@ module.exports = models => async (req, res) => {
       error: `${reqUser.roles ||
         'undefined roles'} cannot invite a ${role} without a collection`,
     })
-    logger.error(`request user does not have any defined roles`)
+    logger.error(`cannot invite manuscript roles without a collection`)
     return
   }
 
@@ -74,7 +74,7 @@ module.exports = models => async (req, res) => {
 
     if (user) {
       res.status(400).json({ error: 'User already exists' })
-      logger.error('admin tried to invite existing user')
+      logger.error('someone tried to invite existing user')
       return
     }
   } catch (e) {
diff --git a/packages/component-invite/src/routes/reset.js b/packages/component-invite/src/routes/reset.js
index cb498f1eb3a9434be192112df5043efefd43dd20..983177fa855832d97dfcc09bca309f27aef86af6 100644
--- a/packages/component-invite/src/routes/reset.js
+++ b/packages/component-invite/src/routes/reset.js
@@ -2,12 +2,30 @@ const logger = require('@pubsweet/logger')
 const helpers = require('../helpers/helpers')
 
 module.exports = models => async (req, res) => {
-  if (!helpers.checkForUndefinedParams(req.body)) {
+  const {
+    email,
+    firstName,
+    lastName,
+    title,
+    affiliation,
+    password,
+    token,
+  } = req.body
+  if (
+    !helpers.checkForUndefinedParams(
+      email,
+      firstName,
+      lastName,
+      title,
+      affiliation,
+      password,
+      token,
+    )
+  ) {
     res.status(400).json({ error: 'missing required params' })
     return
   }
 
-  const { password } = req.body
   if (password.length < 7) {
     res
       .status(400)
@@ -18,18 +36,9 @@ module.exports = models => async (req, res) => {
     return
   }
 
-  const updateFields = {
-    password,
-    firstName: req.body.firstName,
-    lastName: req.body.lastName,
-    affiliation: req.body.affiliation,
-    title: req.body.title,
-    isConfirmed: true,
-  }
-
   const validateResponse = await helpers.validateEmailAndToken(
-    req.body.email,
-    req.body.token,
+    email,
+    token,
     models.User,
   )
   if (validateResponse.success === false) {
@@ -44,6 +53,15 @@ module.exports = models => async (req, res) => {
     return
   }
 
+  const updateFields = {
+    password,
+    firstName,
+    lastName,
+    affiliation,
+    title,
+    isConfirmed: true,
+  }
+
   let newUser = Object.assign(
     validateResponse.user,
     updateFields,
diff --git a/packages/component-invite/src/tests/fixtures/collections.js b/packages/component-invite/src/tests/fixtures/collections.js
index 959e3b413fe82696d18505c60a3e5caafa5a7947..91eaf3b200444735abaac4e29d4561527829b84d 100644
--- a/packages/component-invite/src/tests/fixtures/collections.js
+++ b/packages/component-invite/src/tests/fixtures/collections.js
@@ -1,12 +1,10 @@
-const users = require('./users')
-
 module.exports = {
   standardCollection: {
     id: '2c4fb766-a798-4c32-b857-c5d21a2ab331',
     title: 'Standard Collection',
     type: 'collection',
     fragments: [],
-    owners: [users.admin.id],
+    owners: [],
     save: jest.fn(),
   },
 }
diff --git a/packages/component-invite/src/tests/fixtures/fixtures.js b/packages/component-invite/src/tests/fixtures/fixtures.js
index b0a926aa1427d8662addf9dd3e46c6a61147f915..0ea29e85ac3a373a20a0e82377e0b6f3dfeef51e 100644
--- a/packages/component-invite/src/tests/fixtures/fixtures.js
+++ b/packages/component-invite/src/tests/fixtures/fixtures.js
@@ -1,7 +1,9 @@
 const users = require('./users')
 const collections = require('./collections')
+const teams = require('./teams')
 
 module.exports = {
   users,
   collections,
+  teams,
 }
diff --git a/packages/component-invite/src/tests/fixtures/teams.js b/packages/component-invite/src/tests/fixtures/teams.js
new file mode 100644
index 0000000000000000000000000000000000000000..1410dc9e2ce5475bd299847a0fa1db65dee57f66
--- /dev/null
+++ b/packages/component-invite/src/tests/fixtures/teams.js
@@ -0,0 +1,15 @@
+const users = require('./users')
+
+module.exports = {
+  teamType: {
+    name: 'editorInChief',
+    permissions: 'editor',
+  },
+  group: 'editor',
+  name: 'Editor in Chief',
+  object: {
+    type: 'collection',
+    id: '123',
+  },
+  members: [users.editorInChief.id],
+}
diff --git a/packages/component-invite/src/tests/fixtures/users.js b/packages/component-invite/src/tests/fixtures/users.js
index 6a13d1eb223024f9bb580b5c31e96b96c20c8028..0e207530439ded9a197b616c4f0e74cd8d6ceedd 100644
--- a/packages/component-invite/src/tests/fixtures/users.js
+++ b/packages/component-invite/src/tests/fixtures/users.js
@@ -1,4 +1,4 @@
-module.exports = {
+const users = {
   admin: {
     type: 'user',
     username: 'admin',
@@ -7,4 +7,31 @@ module.exports = {
     admin: true,
     id: 'admin123',
   },
+  editorInChief: {
+    type: 'user',
+    username: 'editor',
+    email: 'editor@example.com',
+    password: 'test1234',
+    admin: false,
+    id: 'editor123',
+    roles: ['editorInChief'],
+    passwordResetToken: 'token123',
+    firstName: 'vlad',
+    lastName: 'dracul',
+    affiliation: 'MIT',
+    title: 'prof',
+    save: jest.fn(() => users.editorInChief),
+    isConfirmed: false,
+  },
+  handlingEditor: {
+    type: 'user',
+    username: 'handling',
+    email: 'handling@example.com',
+    password: 'test',
+    admin: false,
+    id: 'handling123',
+    roles: ['handlingEditor'],
+  },
 }
+
+module.exports = users
diff --git a/packages/component-invite/src/tests/get.test.js b/packages/component-invite/src/tests/get.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..db3b68af7effa3400e2655e0b4762af92f7b63d1
--- /dev/null
+++ b/packages/component-invite/src/tests/get.test.js
@@ -0,0 +1,103 @@
+process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
+process.env.SUPPRESS_NO_CONFIG_WARNING = true
+
+const httpMocks = require('node-mocks-http')
+const fixtures = require('./fixtures/fixtures')
+
+const buildModels = user => {
+  const models = {
+    User: {
+      findByEmail: jest.fn(
+        () =>
+          user instanceof Error ? Promise.reject(user) : Promise.resolve(user),
+      ),
+    },
+  }
+
+  return models
+}
+const user = fixtures.users.editorInChief
+const query = {
+  email: user.email,
+  token: user.passwordResetToken,
+}
+describe('Get inivte data route handler', () => {
+  it('should return success email and token are correct', async () => {
+    const req = httpMocks.createRequest()
+    req.query = query
+    const res = httpMocks.createResponse()
+    const models = buildModels(user)
+    await require('../routes/get')(models)(req, res)
+
+    expect(res.statusCode).toBe(200)
+    const data = JSON.parse(res._getData())
+    expect(data.firstName).toEqual(user.firstName)
+  })
+  it('should return an error when some parameters are missing', async () => {
+    delete query.email
+    const req = httpMocks.createRequest()
+    req.query = query
+
+    const res = httpMocks.createResponse()
+    const models = buildModels(user)
+    await require('../routes/get')(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('missing required params')
+    query.email = user.email
+  })
+  it('should return an error when tokens do not match', async () => {
+    query.token = 'wrongToken'
+    const req = httpMocks.createRequest()
+    req.query = query
+
+    const res = httpMocks.createResponse()
+    const models = buildModels(user)
+    await require('../routes/get')(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('invalid request')
+    query.token = user.passwordResetToken
+  })
+  it('should return an error when user is not found', async () => {
+    const req = httpMocks.createRequest()
+    req.query = query
+    const error = new Error()
+    error.name = 'NotFoundError'
+    error.status = 404
+    const res = httpMocks.createResponse()
+    const models = buildModels(error)
+    await require('../routes/get')(models)(req, res)
+    expect(res.statusCode).toBe(404)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('user not found')
+  })
+  it('should return an error when there is a validation problem', async () => {
+    const req = httpMocks.createRequest()
+    req.query = query
+    const error = new Error()
+    error.name = 'ValidationError'
+    error.status = 400
+    error.details = []
+    error.details.push({ message: 'validation error' })
+    const res = httpMocks.createResponse()
+    const models = buildModels(error)
+    await require('../routes/get')(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('validation error')
+  })
+  it('should return an error when there is a system problem', async () => {
+    const req = httpMocks.createRequest()
+    req.query = query
+    const error = new Error()
+    error.details = []
+    error.details.push({ message: 'internal server error' })
+    const res = httpMocks.createResponse()
+    const models = buildModels(error)
+    await require('../routes/get')(models)(req, res)
+    expect(res.statusCode).toBe(500)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('internal server error')
+  })
+})
diff --git a/packages/component-invite/src/tests/mocks/Team.js b/packages/component-invite/src/tests/mocks/Team.js
new file mode 100644
index 0000000000000000000000000000000000000000..f84ef4dcf8ffa0e5056c8f0eb86573f624d74c06
--- /dev/null
+++ b/packages/component-invite/src/tests/mocks/Team.js
@@ -0,0 +1,16 @@
+/* eslint-disable func-names-any */
+
+function Team(properties) {
+  this.teamType = properties.teamType
+  this.group = properties.group
+  this.name = properties.name
+  this.object = properties.object
+  this.members = properties.members
+}
+
+Team.prototype.save = jest.fn(function saveTeam() {
+  this.id = '111222'
+  return Promise.resolve(this)
+})
+
+module.exports = Team
diff --git a/packages/component-invite/src/tests/post.test.js b/packages/component-invite/src/tests/post.test.js
index dc39ab175b4365b8363250792023d803b4c333ad..e6e2d5c2defe50f3cf3aa836bcc11137e7dea96a 100644
--- a/packages/component-invite/src/tests/post.test.js
+++ b/packages/component-invite/src/tests/post.test.js
@@ -6,11 +6,13 @@ const random = require('lodash/random')
 const fixtures = require('./fixtures/fixtures')
 const UserMock = require('./mocks/User')
 const Chance = require('chance')
+const TeamMock = require('./mocks/Team')
 
 jest.mock('pubsweet-component-mail-service', () => ({ setupEmail: jest.fn() }))
 const chance = new Chance()
 const globalRoles = ['editorInChief', 'author', 'admin']
 const manuscriptRoles = ['handlingEditor', 'reviewer']
+
 const buildModels = (collection, findUser, emailUser) => {
   const models = {
     User: {},
@@ -22,8 +24,8 @@ const buildModels = (collection, findUser, emailUser) => {
             : Promise.resolve(collection),
       ),
     },
+    Team: {},
   }
-
   UserMock.find = jest.fn(
     () =>
       findUser instanceof Error
@@ -38,6 +40,7 @@ const buildModels = (collection, findUser, emailUser) => {
   )
 
   models.User = UserMock
+  models.Team = TeamMock
   return models
 }
 
@@ -51,22 +54,22 @@ const body = {
 }
 body.admin = body.role === 'admin'
 
-const getNotFoundError = () => {
-  const error = new Error()
-  error.name = 'NotFoundError'
-  error.status = 404
+const notFoundError = new Error()
+notFoundError.name = 'NotFoundError'
+notFoundError.status = 404
+
+const adminUser = fixtures.users.admin
+const editorInChiefUser = fixtures.users.editorInChief
+const handlingEditorUser = fixtures.users.handlingEditor
 
-  return error
-}
 describe('Post invite route handler', () => {
-  it('should return success when the admin invites an Editor in Chief, Author or Admin', async () => {
+  it('should return success when the admin invites a global role', async () => {
     const req = httpMocks.createRequest({
       body,
     })
-    req.user = fixtures.users.admin
+    req.user = adminUser
     const res = httpMocks.createResponse()
-    const error = getNotFoundError()
-    const models = buildModels(error, fixtures.users.admin, error)
+    const models = buildModels(notFoundError, adminUser, notFoundError)
     await require('../routes/post')(models)(req, res)
 
     expect(res.statusCode).toBe(200)
@@ -80,11 +83,10 @@ describe('Post invite route handler', () => {
     const req = httpMocks.createRequest({
       body,
     })
-    req.user = fixtures.users.admin
+    req.user = adminUser
     req.params.collectionId = '123'
     const res = httpMocks.createResponse()
-    const error = getNotFoundError()
-    const models = buildModels(error, fixtures.users.admin)
+    const models = buildModels(notFoundError, adminUser)
     await require('../routes/post')(models)(req, res)
     expect(res.statusCode).toBe(403)
     const data = JSON.parse(res._getData())
@@ -94,13 +96,13 @@ describe('Post invite route handler', () => {
   })
   it('should return an error when the admin invites a manuscript role', async () => {
     body.role = manuscriptRoles[random(0, manuscriptRoles.length - 1)]
+    body.admin = false
     const req = httpMocks.createRequest({
       body,
     })
-    req.user = fixtures.users.admin
+    req.user = adminUser
     const res = httpMocks.createResponse()
-    const error = getNotFoundError()
-    const models = buildModels(error, fixtures.users.admin)
+    const models = buildModels(notFoundError, adminUser)
     await require('../routes/post')(models)(req, res)
     expect(res.statusCode).toBe(403)
     const data = JSON.parse(res._getData())
@@ -111,13 +113,105 @@ describe('Post invite route handler', () => {
     const req = httpMocks.createRequest({
       body,
     })
-    req.user = fixtures.users.admin
+    req.user = adminUser
+    const res = httpMocks.createResponse()
+    const models = buildModels(notFoundError, adminUser)
+    await require('../routes/post')(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('Email and role are required')
+    body.email = chance.email()
+  })
+  it('should return an error when the a non-admin invites a globalRole on a collection', async () => {
+    body.role = globalRoles[random(0, globalRoles.length - 1)]
+    body.admin = body.role === 'admin'
+    const req = httpMocks.createRequest({
+      body,
+    })
+    req.user = editorInChiefUser
+    req.params.collectionId = '123'
+    const res = httpMocks.createResponse()
+    const models = buildModels(notFoundError, editorInChiefUser)
+    await require('../routes/post')(models)(req, res)
+    expect(res.statusCode).toBe(403)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual(`Role ${body.role} cannot be set on collections`)
+  })
+  it('should return an error when an non-admin invites without a collection', async () => {
+    body.role = manuscriptRoles[random(0, manuscriptRoles.length - 1)]
+    body.admin = false
+    const req = httpMocks.createRequest({
+      body,
+    })
+    req.user = editorInChiefUser
+    const res = httpMocks.createResponse()
+
+    const models = buildModels(notFoundError, editorInChiefUser)
+    await require('../routes/post')(models)(req, res)
+    expect(res.statusCode).toBe(403)
+    // console.log(res._getData())
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual(
+      `${req.user.roles} cannot invite a ${body.role} without a collection`,
+    )
+  })
+  it('should return an error when inviting an existing user', async () => {
+    body.role = globalRoles[random(0, globalRoles.length - 1)]
+    body.admin = body.role === 'admin'
+    const req = httpMocks.createRequest({
+      body,
+    })
+    req.user = adminUser
     const res = httpMocks.createResponse()
-    const error = getNotFoundError()
-    const models = buildModels(error, fixtures.users.admin)
+    const models = buildModels(notFoundError, adminUser, editorInChiefUser)
     await require('../routes/post')(models)(req, res)
+
     expect(res.statusCode).toBe(400)
     const data = JSON.parse(res._getData())
-    expect(data.error).toEqual(`Email and role are required`)
+    expect(data.error).toEqual('User already exists')
+  })
+  it('should return success when the editor in chief invites a handling Editor with a collection', async () => {
+    body.role = 'handlingEditor'
+    body.admin = false
+    const req = httpMocks.createRequest({
+      body,
+    })
+    req.user = editorInChiefUser
+    req.params.collectionId = '123'
+    const res = httpMocks.createResponse()
+    const models = buildModels(
+      fixtures.collections.standardCollection,
+      editorInChiefUser,
+      notFoundError,
+    )
+    await require('../routes/post')(models)(req, res)
+
+    expect(res.statusCode).toBe(200)
+    const data = JSON.parse(res._getData())
+    expect(data.roles[0]).toEqual(body.role)
+    expect(data.firstName).toEqual(body.firstName)
+    expect(data.email).toEqual(body.email)
+  })
+  it('should return success when the handlintEditor invites a reviewer with a collection', async () => {
+    body.role = 'reviewer'
+    body.admin = false
+    const req = httpMocks.createRequest({
+      body,
+    })
+    req.user = handlingEditorUser
+    req.params.collectionId = '123'
+    const res = httpMocks.createResponse()
+    const models = buildModels(
+      fixtures.collections.standardCollection,
+      handlingEditorUser,
+      notFoundError,
+    )
+    await require('../routes/post')(models)(req, res)
+
+    expect(res.statusCode).toBe(200)
+    const data = JSON.parse(res._getData())
+    expect(data.roles[0]).toEqual(body.role)
+    expect(data.firstName).toEqual(body.firstName)
+    expect(data.email).toEqual(body.email)
   })
 })
diff --git a/packages/component-invite/src/tests/reset.test.js b/packages/component-invite/src/tests/reset.test.js
new file mode 100644
index 0000000000000000000000000000000000000000..b2ea34c15e50105b1e904834bb9c58fb695ce1c8
--- /dev/null
+++ b/packages/component-invite/src/tests/reset.test.js
@@ -0,0 +1,125 @@
+process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
+process.env.SUPPRESS_NO_CONFIG_WARNING = true
+
+const httpMocks = require('node-mocks-http')
+const fixtures = require('./fixtures/fixtures')
+const UserMock = require('./mocks/User')
+
+const buildModels = user => {
+  const models = {
+    User: {},
+  }
+  UserMock.findByEmail = jest.fn(
+    () =>
+      user instanceof Error ? Promise.reject(user) : Promise.resolve(user),
+  )
+
+  models.User = UserMock
+  return models
+}
+
+const editorInChiefUser = fixtures.users.editorInChief
+const body = {
+  email: editorInChiefUser.email,
+  firstName: editorInChiefUser.firstName,
+  lastName: editorInChiefUser.lastName,
+  title: editorInChiefUser.email,
+  affiliation: editorInChiefUser.email,
+  password: editorInChiefUser.password,
+  token: editorInChiefUser.passwordResetToken,
+  isConfirmed: false,
+}
+
+const notFoundError = new Error()
+notFoundError.name = 'NotFoundError'
+notFoundError.status = 404
+
+describe('Password reset after invite route handler', () => {
+  it('should return success when the body is correct', async () => {
+    const req = httpMocks.createRequest({ body })
+    const res = httpMocks.createResponse()
+    const models = buildModels(editorInChiefUser)
+    await require('../routes/reset')(models)(req, res)
+
+    expect(res.statusCode).toBe(200)
+    const data = JSON.parse(res._getData())
+    expect(data.firstName).toEqual(body.firstName)
+    expect(data.email).toEqual(body.email)
+    editorInChiefUser.passwordResetToken = 'token123'
+  })
+  it('should return an error when some parameters are missing', async () => {
+    delete body.email
+    const req = httpMocks.createRequest({ body })
+
+    const res = httpMocks.createResponse()
+    const models = buildModels(editorInChiefUser)
+    await require('../routes/reset')(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('missing required params')
+    body.email = editorInChiefUser.email
+  })
+  it('should return an error when the password is too small', async () => {
+    body.password = 'small'
+    const req = httpMocks.createRequest({ body })
+
+    const res = httpMocks.createResponse()
+    const models = buildModels(editorInChiefUser)
+    await require('../routes/reset')(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual(
+      'password needs to be at least 7 characters long',
+    )
+    body.password = editorInChiefUser.password
+  })
+  it('should return an error when user is not found', async () => {
+    const req = httpMocks.createRequest({ body })
+    const error = new Error()
+    error.name = 'NotFoundError'
+    error.status = 404
+    const res = httpMocks.createResponse()
+    const models = buildModels(error)
+    await require('../routes/reset')(models)(req, res)
+    expect(res.statusCode).toBe(404)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('user not found')
+  })
+  it('should return an error when there is a validation problem', async () => {
+    const req = httpMocks.createRequest({ body })
+    const error = new Error()
+    error.name = 'ValidationError'
+    error.status = 400
+    error.details = []
+    error.details.push({ message: 'validation error' })
+    const res = httpMocks.createResponse()
+    const models = buildModels(error)
+    await require('../routes/reset')(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('validation error')
+  })
+  it('should return an error when there is a system problem', async () => {
+    const req = httpMocks.createRequest({ body })
+    const error = new Error()
+    error.details = []
+    error.details.push({ message: 'internal server error' })
+    const res = httpMocks.createResponse()
+    const models = buildModels(error)
+    await require('../routes/reset')(models)(req, res)
+    expect(res.statusCode).toBe(500)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('internal server error')
+  })
+  it('should return an error when the user is already confirmed', async () => {
+    editorInChiefUser.isConfirmed = true
+    const req = httpMocks.createRequest({ body })
+    const res = httpMocks.createResponse()
+    const models = buildModels(editorInChiefUser)
+    await require('../routes/reset')(models)(req, res)
+    expect(res.statusCode).toBe(400)
+    const data = JSON.parse(res._getData())
+    expect(data.error).toEqual('User is already confirmed')
+    editorInChiefUser.isConfirmed = false
+  })
+})
diff --git a/packages/components-faraday/src/components/Dashboard/Dashboard.js b/packages/components-faraday/src/components/Dashboard/Dashboard.js
index fbf0cdcb4767a840ceba2db862f9d584d549b3e9..6fb0797f1e44e513604adfb3cc14886fd4738687 100644
--- a/packages/components-faraday/src/components/Dashboard/Dashboard.js
+++ b/packages/components-faraday/src/components/Dashboard/Dashboard.js
@@ -10,12 +10,10 @@ import DashboardItems from './DashboardItems'
 import DashboardFilters from './DashboardFilters'
 
 const Dashboard = ({
-  changeViewMode,
   createDraftSubmission,
   currentUser,
   dashboard,
   deleteProject,
-  listView,
   filters,
   getItems,
   getFilterOptions,
@@ -36,14 +34,11 @@ const Dashboard = ({
     </div>
     <DashboardFilters
       changeFilterValue={changeFilterValue}
-      changeView={changeViewMode}
       getFilterOptions={getFilterOptions}
-      listView={listView}
     />
     <DashboardItems
       deleteProject={deleteProject}
       list={getItems()}
-      listView={listView}
       showAbstractModal={showAbstractModal}
     />
   </div>
diff --git a/packages/components-faraday/src/components/Dashboard/DashboardCard.js b/packages/components-faraday/src/components/Dashboard/DashboardCard.js
index 08596722864c7401152e8ed74f8b983dda189ac8..e0e31e49ee212dea98f30329d2e4fe8909e20ff5 100644
--- a/packages/components-faraday/src/components/Dashboard/DashboardCard.js
+++ b/packages/components-faraday/src/components/Dashboard/DashboardCard.js
@@ -1,125 +1,353 @@
 import React from 'react'
-import classnames from 'classnames'
+import PropTypes from 'prop-types'
 import { get, isEmpty } from 'lodash'
 import styled from 'styled-components'
 import { Button, Icon } from '@pubsweet/ui'
+import { compose, getContext } from 'recompose'
 
 import { parseVersion, getFilesURL, downloadAll } from './utils'
-import classes from './Dashboard.local.scss'
 
 const DashboardCard = ({
   deleteProject,
   history,
-  listView,
   project,
   version,
   showAbstractModal,
+  journal,
+  ...rest
 }) => {
-  const { submitted, author, title, type, version: vers } = parseVersion(
-    version,
-  )
+  const { submitted, title, type, version: vers } = parseVersion(version)
   const files = getFilesURL(get(version, 'files'))
   const status = get(project, 'status') || 'Draft'
   const hasFiles = !isEmpty(files)
   const abstract = get(version, 'metadata.abstract')
   const metadata = get(version, 'metadata')
-
   return (
-    <div className={classes.card}>
-      <div className={classes.leftSide}>
-        <div
-          className={classes.title}
-          dangerouslySetInnerHTML={{ __html: title }} // eslint-disable-line
-        />
-
-        <div className={classes.quickInfo}>
-          <div className={classes.status}>{status}</div>
-          <div className={classes.version}>{`v${vers} ${
-            submitted ? `- updated on ${submitted}` : ''
-          }`}</div>
-        </div>
-      </div>
-      <div className={classes.rightSide}>
-        <div
-          className={classnames({
-            [classes.disabled]: !hasFiles,
-            [classes.pointer]: true,
-          })}
-          onClick={() => (hasFiles ? downloadAll(files) : null)}
-        >
-          <Icon>download</Icon>
-        </div>
-        <div className={classes.pointer} onClick={() => deleteProject(project)}>
-          <Icon>trash-2</Icon>
-        </div>
-        <div
-          className={classes.pointer}
-          onClick={() =>
-            history.push(
-              `/projects/${project.id}/versions/${version.id}/submit`,
-            )
-          }
-        >
-          <Icon>file-text</Icon>
-        </div>
-      </div>
-      {!listView && (
-        <div className={classes.expandedView}>
-          <div className={classes.column3}>
-            <div className={classes.column2}>
-              <div>Submission author</div>
-              <div>Abstract</div>
+    <Card>
+      <ListView>
+        <Left>
+          <Title
+            dangerouslySetInnerHTML={{ __html: title }} // eslint-disable-line
+          />
+          <ManuscriptInfo>
+            <div>
+              <Status>{status}</Status>
+              <DateField>{submitted || ''}</DateField>
             </div>
-            <div className={classes.column2}>
-              <div>{author}</div>
-              {abstract && (
-                <ViewAbstractContainer onClick={showAbstractModal(metadata)}>
-                  <Icon color="#667080" size={18}>
-                    eye
-                  </Icon>
-                  <span>View</span>
-                </ViewAbstractContainer>
-              )}
+            <div>
+              <Version>{`v${vers} - `}</Version>
+              <ManuscriptId>{`ID ${version.id.split('-')[0]}`}</ManuscriptId>
+              <ManuscriptType>{type}</ManuscriptType>
             </div>
+          </ManuscriptInfo>
+        </Left>
+        <Right>
+          <ClickableIcon
+            disabled={!hasFiles}
+            onClick={() => (hasFiles ? downloadAll(files) : null)}
+          >
+            <Icon>download</Icon>
+          </ClickableIcon>
+          <ClickableIcon onClick={() => deleteProject(project)}>
+            <Icon>trash-2</Icon>
+          </ClickableIcon>
+          <ClickableIcon>
+            <Icon>more-horizontal</Icon>
+          </ClickableIcon>
+          <Details
+            onClick={() =>
+              history.push(
+                `/projects/${project.id}/versions/${version.id}/submit`,
+              )
+            }
+          >
+            Details
+            <Icon color="#667080">chevron-right</Icon>
+          </Details>
+        </Right>
+      </ListView>
+      <DetailsView>
+        <LeftDetails>
+          <JournalTitle>{journal.metadata.nameText}</JournalTitle>
+          <Issue>
+            {journal.issueTypes.find(t => t.value === metadata.issue).label}
+          </Issue>
+          {get(version, 'authors') && (
+            <Authors>
+              <span>Authors:</span>
+              <AuthorList>
+                {version.authors
+                  .map(({ firstName, lastName }) => `${firstName} ${lastName}`)
+                  .join(', ')}
+              </AuthorList>
+            </Authors>
+          )}
+          <PreviewContainer>
+            {abstract && (
+              <ClickableIconContainer onClick={showAbstractModal(metadata)}>
+                <Icon color="#667080" size={18}>
+                  eye
+                </Icon>
+                <span>Abstract</span>
+              </ClickableIconContainer>
+            )}
+            <ClickableIconContainer>
+              <Icon color="#667080" size={18}>
+                eye
+              </Icon>
+              <span>Cover letter</span>
+            </ClickableIconContainer>
+          </PreviewContainer>
+        </LeftDetails>
+        <RightDetails>
+          <div>
+            <Label>Handling editor</Label>
+            <ActionButtons>ASSIGN</ActionButtons>
           </div>
-          <div className={classes.column3}>
-            <div className={classes.column2}>
-              <div>Submitted On</div>
-              <div>Type</div>
-            </div>
-            <div className={classes.column2}>
-              <div>{submitted}</div>
-              <div>
-                <span className={classes.status}>{type}</span>
-              </div>
-            </div>
+          <div>
+            <Label>Reviewers</Label>
+            <ActionButtons>INVITE</ActionButtons>
           </div>
-          <div className={classes.column3}>
-            <div className={classes.column2}>
-              <div>Handling Editor</div>
-              <div>Reviewers</div>
-            </div>
-            <div className={classes.column2}>
-              <Button className={classes.button} primary>
-                Invite
-              </Button>
-              <Button className={classes.button} primary>
-                Invite
-              </Button>
-            </div>
-          </div>
-        </div>
-      )}
-    </div>
+        </RightDetails>
+      </DetailsView>
+    </Card>
   )
 }
 
-const ViewAbstractContainer = styled.div`
+export default compose(getContext({ journal: PropTypes.object }))(DashboardCard)
+
+// #region styled-components
+const PreviewContainer = styled.div`
+  display: flex;
+  margin-top: 18px;
+`
+
+const AuthorList = styled.span`
+  color: #667080;
+  font-family: Helvetica;
+  font-size: 14px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  text-align: left;
+  white-space: nowrap;
+  max-width: 400px;
+  width: 400px;
+`
+
+const Authors = styled.div`
+  align-items: center;
+  display: flex;
+  flex-direction: row;
+  justify-content: flex-start;
+  margin-top: 15px;
+
+  span:first-child {
+    color: #667080;
+    font-family: Helvetica;
+    font-size: 12px;
+    margin-right: 8px;
+    text-align: left;
+    text-transform: uppercase;
+  }
+`
+
+const ActionButtons = styled(Button)`
+  align-items: center;
+  background-color: #667080;
+  display: flex;
+  height: 20px;
+  padding: 4px 8px;
+  font-family: Helvetica;
+  font-size: 12px;
+  text-align: center;
+  color: #ffffff;
+`
+
+const LeftDetails = styled.div`
+  display: flex;
+  flex: 3;
+  flex-direction: column;
+  justify-content: flex-start;
+  padding: 10px 20px;
+`
+
+const RightDetails = styled.div`
+  display: flex;
+  flex: 2;
+  flex-direction: column;
+
+  div {
+    align-items: center;
+    display: flex;
+    flex-direction: row;
+    margin: 6px 0;
+  }
+`
+
+const Label = styled.span`
+  color: #667080;
+  font-family: Helvetica;
+  font-size: 12px;
+  text-align: left;
+  text-transform: uppercase;
+  width: 150px;
+`
+
+const JournalTitle = styled.span`
+  color: #667080;
+  font-family: Helvetica;
+  font-size: 14px;
+  font-weight: bold;
+  text-align: left;
+`
+
+const Issue = styled.span`
+  color: #667080;
+  font-family: Helvetica;
+  font-size: 14px;
+  text-align: left;
+`
+
+const DetailsView = styled.div`
+  align-items: center;
+  border-top: 1px solid #667080;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  width: 100%;
+`
+
+const ListView = styled.div`
   align-items: center;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  width: 100%;
+`
+
+const ManuscriptId = styled.span`
+  color: #667080;
+  font-family: Helvetica;
+  font-size: 12px;
+  margin-left: 8px;
+  text-align: left;
+  text-decoration: underline;
+  text-transform: uppercase;
+`
+
+const Version = styled.span`
+  color: #667080;
+  font-family: Helvetica;
+  font-size: 13px;
+  text-align: left;
+`
+const Details = styled.div`
+  align-items: center;
+  color: #667080;
   cursor: pointer;
   display: flex;
+  font-family: Helvetica;
+  font-size: 14px;
+  margin-left: 8px;
+  text-decoration: underline;
+  text-align: center;
+`
+
+const ClickableIcon = styled.div`
+  cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
+  margin: 0 7px;
+
+  svg {
+    stroke: ${({ disabled }) => (disabled ? '#eee' : '#667080')};
+  }
+`
+
+const Card = styled.div`
+  align-items: center;
+  border: 1px solid #667080;
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-start;
+  margin-bottom: 10px;
+`
 
-  & > span {
+const Right = styled.div`
+  align-items: center;
+  flex: 1;
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  margin: 0 15px;
+`
+
+const Left = styled.div`
+  border-right: 1px solid #667080;
+  display: flex;
+  flex-direction: column;
+  flex: 5;
+  margin: 10px 0;
+  padding: 0 10px;
+`
+
+const ManuscriptInfo = styled.div`
+  align-items: center;
+  display: flex;
+  justify-content: space-between;
+
+  div {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+  }
+`
+
+const ManuscriptType = styled.div`
+  border: 1px solid #667080;
+  color: #667080;
+  font-family: Helvetica;
+  font-size: 12px;
+  font-weight: bold;
+  padding: 6px 4px;
+  margin-left: 10px;
+  text-align: left;
+  text-transform: uppercase;
+`
+
+const Title = styled.span`
+  color: #667080;
+  font-family: Helvetica;
+  font-size: 18px;
+  text-align: left;
+`
+
+const Status = styled.div`
+  border: 1px solid #667080;
+  color: #667080;
+  font-family: Helvetica;
+  font-size: 12px;
+  font-weight: bold;
+  text-align: left;
+  margin: 0.5em 0;
+  padding: 0.2em 0.5em;
+  text-transform: uppercase;
+`
+
+const DateField = styled.span`
+  color: #667080;
+  font-family: Helvetica;
+  font-size: 13px;
+  margin: 0 8px;
+  text-align: left;
+`
+
+const ClickableIconContainer = styled.div`
+  align-items: center;
+  cursor: pointer;
+  display: flex;
+  margin-right: 8px;
+
+  span:last-child {
     color: #667080;
     font-family: Helvetica;
     font-size: 14px;
@@ -128,5 +356,4 @@ const ViewAbstractContainer = styled.div`
     text-decoration: underline;
   }
 `
-
-export default DashboardCard
+// #endregion
diff --git a/packages/components-faraday/src/components/Dashboard/DashboardFilters.js b/packages/components-faraday/src/components/Dashboard/DashboardFilters.js
index 3a2ae74005be770013c39138013a188174111c5b..b7ca2b2afd595966fbbc1eb96f26ab3313ac3e73 100644
--- a/packages/components-faraday/src/components/Dashboard/DashboardFilters.js
+++ b/packages/components-faraday/src/components/Dashboard/DashboardFilters.js
@@ -1,5 +1,5 @@
 import React from 'react'
-import { Icon, Menu } from '@pubsweet/ui'
+import { Menu } from '@pubsweet/ui'
 import { compose, withHandlers } from 'recompose'
 
 import classes from './Dashboard.local.scss'
@@ -13,7 +13,6 @@ const DashboardFilters = ({
   view,
   status,
   createdAt,
-  changeView,
   listView,
   changeFilter,
   changeSort,
@@ -42,12 +41,6 @@ const DashboardFilters = ({
         <Menu onChange={changeSort} options={sortOptions} />
       </div>
     </div>
-    <div className={classes.viewMode} onClick={changeView}>
-      <div className={classes.icon}>
-        {listView ? <Icon>list</Icon> : <Icon>credit-card</Icon>}
-      </div>
-      {listView ? ' List' : ' Card'} View
-    </div>
   </div>
 )
 
diff --git a/packages/components-faraday/src/components/Dashboard/DashboardPage.js b/packages/components-faraday/src/components/Dashboard/DashboardPage.js
index 4154a7b1ba1bfcf8084436e3f45bd38d9535aa0c..f183c434e5ebc1f5bc323565b7b3b6480634c1f5 100644
--- a/packages/components-faraday/src/components/Dashboard/DashboardPage.js
+++ b/packages/components-faraday/src/components/Dashboard/DashboardPage.js
@@ -1,9 +1,11 @@
 import { get } from 'lodash'
+import PropTypes from 'prop-types'
 import { connect } from 'react-redux'
 import { actions } from 'pubsweet-client'
+import { withJournal } from 'xpub-journal'
 import { ConnectPage } from 'xpub-connect'
 import { withRouter } from 'react-router-dom'
-import { compose, withState, withHandlers } from 'recompose'
+import { compose, withContext } from 'recompose'
 import { newestFirst, selectCurrentUser } from 'xpub-selectors'
 import { createDraftSubmission } from 'pubsweet-component-wizard/src/redux/conversion'
 
@@ -17,10 +19,6 @@ export default compose(
     actions.getTeams(),
     actions.getUsers(),
   ]),
-  withState('listView', 'changeView', true),
-  withHandlers({
-    changeViewMode: ({ changeView }) => () => changeView(listView => !listView),
-  }),
   connect(
     state => {
       const { collections, conversion } = state
@@ -52,6 +50,7 @@ export default compose(
     }),
   ),
   withRouter,
+  withJournal,
   withFilters({
     status: {
       options: [
@@ -86,4 +85,10 @@ export default compose(
       },
     },
   }),
+  withContext(
+    {
+      journal: PropTypes.object,
+    },
+    ({ journal }) => ({ journal }),
+  ),
 )(Dashboard)
diff --git a/packages/components-faraday/src/components/Dashboard/utils.js b/packages/components-faraday/src/components/Dashboard/utils.js
index 50d39d73b6082c05a4efe3bda58ae82d0335cccc..84a1700491ccc80f1a0cd3a4cccaa80a9b68c962 100644
--- a/packages/components-faraday/src/components/Dashboard/utils.js
+++ b/packages/components-faraday/src/components/Dashboard/utils.js
@@ -53,7 +53,14 @@ export const parseType = version => {
 
 export const parseSubmissionDate = version => {
   const submitted = get(version, 'submitted')
-  return submitted ? moment(submitted).format('DD-MM-YYYY') : 'N/A'
+  const submittedDate = moment(submitted)
+  const today = moment()
+  const daysAgo = moment.duration(today - moment(submitted)).days()
+  return submitted
+    ? `${submittedDate.format('DD.MM.YYYY')} ${
+        daysAgo > 0 ? `(${daysAgo} days)` : ''
+      }`
+    : 'N/A'
 }
 
 export const parseVersion = version => ({
diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js
index fd1592d877fbab18168c974cf4fe37cba7fd327c..c78cdecc488485f1bf753ea57efbc6ce4dd2da05 100644
--- a/packages/xpub-faraday/config/default.js
+++ b/packages/xpub-faraday/config/default.js
@@ -26,7 +26,7 @@ module.exports = {
   'pubsweet-client': {
     API_ENDPOINT: '/api',
     'login-redirect': '/',
-    'redux-log': true,
+    'redux-log': false,
     theme: process.env.PUBSWEET_THEME,
   },
   'mail-transport': {