diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c2c4a42c4525c9a0e89418749f816bfa12b65c2c..ce94dec3cf1c5bbb4c78a48efd8ada77edc0478c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -16,12 +16,12 @@ build:
     - docker:19.03.12-dind
   stage: build
   script:
-    - docker version
-    - docker build -t $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA .
     - if [ -z "$DOCKERHUB_USERNAME" ] || [ -z "$DOCKERHUB_PASSWORD" ]; then echo "Not pushing" && exit 0; fi
     - docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
-    - echo "Ignore warning! Cannot perform an interactive login from a non TTY device"
+    - docker pull $IMAGE_ORG/$IMAGE_NAME:latest || true
+    - docker build --cache-from $CI_REGISTRY_IMAGE:latest --tag $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA --tag $IMAGE_ORG/$IMAGE_NAME:latest .
     - docker push $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA
+    - docker push $IMAGE_ORG/$IMAGE_NAME:latest
 
 lint:
   image: $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA
@@ -32,7 +32,7 @@ lint:
     - cd ${HOME}
     - npm run lint
 
-test:
+test-chrome:
   image: $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA
   stage: test
   variables:
@@ -49,4 +49,25 @@ test:
   script:
     - cd ${HOME}
     # specify host here else it confuses the linked postgres image
-    - PGHOST=postgres yarn test:all
+    - PGHOST=postgres yarn test:all:chrome
+
+
+test-firefox:
+  allow_failure: true # at this point Cypress' support for Firefox is not stable
+  image: $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA
+  stage: test
+  variables:
+    GIT_STRATEGY: none
+    # setup data for postgres image
+    POSTGRES_USER: test
+    POSTGRES_PASSWORD: pw
+    # connection details for tests
+    PGUSER: test
+    PGPASSWORD: pw
+    NODE_ENV: test
+  services:
+    - postgres
+  script:
+    - cd ${HOME}
+    # specify host here else it confuses the linked postgres image
+    - PGHOST=postgres yarn test:all:firefox
diff --git a/Dockerfile b/Dockerfile
index f76e8697c4626a8e74f9ee45ed27f753a3193b70..7992d9c6e0d025ade4417c1890a1ee041b6cf2ec 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:12
+FROM cypress/browsers:node12.18.0-chrome83-ff77
 
 ENV HOME "/home/simplej"
 RUN mkdir -p ${HOME}
@@ -6,18 +6,19 @@ WORKDIR ${HOME}
 
 ENV NODE_ENV "development"
 
-# Install dependencies for Cypress
-RUN apt-get -y update && apt-get -y install xvfb libgtk-3-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2
-
-COPY . .
+# Only copy things needed for the yarn install
+COPY package.json yarn.lock ./
 
 # We do a development install because react-styleguidist is a dev dependency and we want to run tests
 RUN [ "yarn", "install", "--frozen-lockfile" ]
 
 ENV NODE_ENV ${NODE_ENV}
 
-RUN [ "npx", "pubsweet", "build"]
+# Disabling the build for now, as it runs in the test server again
+# RUN [ "npx", "pubsweet", "build"]
 
+# The copy everything else that changes frequently
+COPY . .
 EXPOSE ${PORT}
 
 CMD []
diff --git a/app/components/AdminPage.js b/app/components/AdminPage.js
index 1db40bc5a12a2bb20cf5deb786f9289d84ba4945..b0f1f8e3bc992dcba479c0428d9b26dd2a600032 100644
--- a/app/components/AdminPage.js
+++ b/app/components/AdminPage.js
@@ -20,7 +20,6 @@ import SubmitPage from '../components/component-submit/src/components/SubmitPage
 import ManuscriptPage from '../components/component-manuscript/src/components/ManuscriptPage'
 import ReviewersPage from '../components/component-review/src/components/ReviewersPage'
 import ReviewPage from '../components/component-review/src/components/ReviewPage'
-import TeamPage from '../components/component-teams-manager/src/components/TeamsManagerPage'
 import DecisionPage from '../components/component-review/src/components/DecisionPage'
 import FormBuilderPage from '../components/component-formbuilder/src/components/FormBuilderPage'
 import NewSubmissionPage from '../components/component-submit/src/components/NewSubmissionPage'
@@ -164,7 +163,6 @@ const AdminPage = ({ children, history, match }) => {
           exact
           path="/journal/versions/:version/submit"
         />
-        <PrivateRoute component={TeamPage} exact path="/journal/admin/teams" />
         <PrivateRoute
           component={FormBuilderPage}
           exact
@@ -183,12 +181,12 @@ const AdminPage = ({ children, history, match }) => {
         <PrivateRoute
           component={ReviewPage}
           exact
-          path="/journal/versions/:version/reviews/:review"
+          path="/journal/versions/:version/review"
         />
         <PrivateRoute
           component={DecisionPage}
           exact
-          path="/journal/versions/:version/decisions/:decision"
+          path="/journal/versions/:version/decision"
         />
         <PrivateRoute component={Profile} exact path="/journal/profile" />
         <PrivateRoute component={UsersManager} path="/journal/admin/users" />
diff --git a/app/components/component-dashboard/src/components/Dashboard.js b/app/components/component-dashboard/src/components/Dashboard.js
index 899780e5f54c8e17dc9f24885d091077f2a20d64..ff23f5449eb3523618468b0fdb7da69d9cb4d038 100644
--- a/app/components/component-dashboard/src/components/Dashboard.js
+++ b/app/components/component-dashboard/src/components/Dashboard.js
@@ -41,7 +41,7 @@ const Dashboard = ({ history, ...props }) => {
   })
 
   if (loading) return <Spinner />
-  if (error) return error
+  if (error) return JSON.stringify(error)
   const dashboard = (data && data.manuscripts) || []
   const currentUser = data && data.currentUser
 
diff --git a/app/components/component-dashboard/src/components/Reviews.js b/app/components/component-dashboard/src/components/Reviews.js
index c53d7a1f0506401d442fa6e6fac7a1e3be2a6ab6..66bd855f6ee05d4d173f88c88054893763539348 100644
--- a/app/components/component-dashboard/src/components/Reviews.js
+++ b/app/components/component-dashboard/src/components/Reviews.js
@@ -29,20 +29,10 @@ const getUserFromTeam = (version, role) => {
 }
 
 const countStatus = (version, status) => {
-  const teamMember = getUserFromTeam(version, 'reviewer')
-
-  if (status === 'rejected' || status === 'invited') {
-    return sumBy(teamMember, member => (member.status === status ? 1 : 0))
-  }
-
-  if (status === 'accepted') {
-    return sumBy(version.reviews, review => (review.recommendation ? 0 : 1))
+  const teamMembers = getUserFromTeam(version, 'reviewer')
+  if (teamMembers) {
+    return sumBy(teamMembers, member => (member.status === status ? 1 : 0))
   }
-
-  if (status === 'completed') {
-    return sumBy(version.reviews, review => (review.recommendation ? 1 : 0))
-  }
-
   return 0
 }
 
diff --git a/app/components/component-dashboard/src/components/sections/EditorItem.js b/app/components/component-dashboard/src/components/sections/EditorItem.js
index b6abd5d49a4f11c221ff25e104bd5bffa70c0754..35e17175110c16a34cf587d4fc0dd605434eecc6 100644
--- a/app/components/component-dashboard/src/components/sections/EditorItem.js
+++ b/app/components/component-dashboard/src/components/sections/EditorItem.js
@@ -28,7 +28,7 @@ const EditorItemLinks = ({ version }) => (
     <Action to={`/journal/versions/${version.id}/submit`}>Summary Info</Action>
     <Action
       data-testid="control-panel"
-      to={`/journal/versions/${version.id}/decisions/${version.id}`}
+      to={`/journal/versions/${version.id}/decision`}
     >
       {version.decision && version.decision.status === 'submitted'
         ? `Decision: ${version.decision.recommendation}`
diff --git a/app/components/component-dashboard/src/components/sections/ReviewerItem.js b/app/components/component-dashboard/src/components/sections/ReviewerItem.js
index 01a8fceafe9b51e023d4cbf2a19bdf5b68376258..7856a9e98fb7187774a6ce40325491b1287f3645 100644
--- a/app/components/component-dashboard/src/components/sections/ReviewerItem.js
+++ b/app/components/component-dashboard/src/components/sections/ReviewerItem.js
@@ -24,7 +24,7 @@ const ReviewerItem = ({ version, journals, currentUser, reviewerRespond }) => {
 
       {(status === 'accepted' || status === 'completed') && (
         <ActionGroup>
-          <Action to={`/journal/versions/${version.id}/reviews`}>
+          <Action to={`/journal/versions/${version.id}/review`}>
             {status === 'completed' ? 'Completed' : 'Do Review'}
           </Action>
         </ActionGroup>
diff --git a/app/components/component-dashboard/src/graphql/mutations/index.js b/app/components/component-dashboard/src/graphql/mutations/index.js
index 019eceb78ba1b28f61adcd3fa8ef7334c72aabd3..cd9b489b448ae0f4560b78e5c8c8685a4da63b43 100644
--- a/app/components/component-dashboard/src/graphql/mutations/index.js
+++ b/app/components/component-dashboard/src/graphql/mutations/index.js
@@ -16,9 +16,8 @@ export default {
         id
         role
         name
-        object {
-          objectId
-          objectType
+        manuscript {
+          id
         }
         members {
           id
@@ -50,9 +49,8 @@ export default {
           id
           role
           name
-          object {
-            objectId
-            objectType
+          manuscript {
+            id
           }
           members {
             id
diff --git a/app/components/component-dashboard/src/graphql/queries/index.js b/app/components/component-dashboard/src/graphql/queries/index.js
index 14952c20c8e356ccac9810bb8931904ce3a9aa3d..f4c9a923cc0549f844ebf44c0611999cbd13f4bc 100644
--- a/app/components/component-dashboard/src/graphql/queries/index.js
+++ b/app/components/component-dashboard/src/graphql/queries/index.js
@@ -15,6 +15,7 @@ export default {
           id
         }
         reviews {
+          id
           open
           recommendation
           created
@@ -28,9 +29,8 @@ export default {
           id
           role
           name
-          object {
-            objectId
-            objectType
+          manuscript {
+            id
           }
           members {
             id
diff --git a/app/components/component-manuscripts/src/Manuscript.jsx b/app/components/component-manuscripts/src/Manuscript.jsx
index 40ee291243c384716a9050955d278d038e398612..f1b4113583d0343a2eb256c35d382ab08d037740 100644
--- a/app/components/component-manuscripts/src/Manuscript.jsx
+++ b/app/components/component-manuscripts/src/Manuscript.jsx
@@ -51,7 +51,7 @@ const User = ({ manuscript }) => {
         )}
       </Cell>
       <LastCell>
-        <Action to={`/journal/versions/${manuscript.id}/decisions/1`}>
+        <Action to={`/journal/versions/${manuscript.id}/decision`}>
           Control
         </Action>
         <Action to={`/journal/versions/${manuscript.id}/manuscript`}>
diff --git a/app/components/component-review/src/components/DecisionPage.js b/app/components/component-review/src/components/DecisionPage.js
index cbb412e57fe6d250203fd9ba976759c5f82b849a..bb7a6c97b8e528890f038484e6ea30975bc94dc6 100644
--- a/app/components/component-review/src/components/DecisionPage.js
+++ b/app/components/component-review/src/components/DecisionPage.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { useRef, useEffect } from 'react'
 import moment from 'moment'
 
 import { Tabs } from '@pubsweet/ui'
@@ -15,7 +15,6 @@ import { AdminSection, Columns, Manuscript, Chat } from './style'
 
 import { Spinner } from '../../../shared'
 
-import { getCommentContent } from './review/util'
 import MessageContainer from '../../../component-chat/src'
 
 const addEditor = (manuscript, label) => ({
@@ -24,23 +23,34 @@ const addEditor = (manuscript, label) => ({
   label,
 })
 
+const commentFields = `
+  id
+  commentType
+  content
+  files {
+    id
+    created
+    label
+    filename
+    fileType
+    mimeType
+    size
+    url
+  }
+`
+
 const reviewFields = `
   id
   created
   updated
-  comments {
-    type
-    content
-    files {
-      id
-      created
-      label
-      filename
-      fileType
-      mimeType
-      size
-      url
-    }
+  decisionComment {
+    ${commentFields}
+  }
+  reviewComment {
+    ${commentFields}
+  }
+  confidentialComment {
+    ${commentFields}
   }
   isDecision
   recommendation
@@ -71,9 +81,8 @@ const fragmentFields = `
     id
     name
     role
-    object {
-      objectId
-      objectType
+    manuscript {
+      id
     }
     members {
       id
@@ -153,29 +162,6 @@ const updateReviewMutationQuery = gql`
   }
 `
 
-const uploadReviewFilesMutation = gql`
-  mutation($file: Upload!) {
-    upload(file: $file) {
-      url
-    }
-  }
-`
-
-// const createFileMutation = gql`
-//   mutation($file: Upload!) {
-//     createFile(file: $file) {
-//       id
-//       created
-//       label
-//       filename
-//       fileType
-//       mimeType
-//       size
-//       url
-//     }
-//   }
-// `
-
 const makeDecisionMutation = gql`
   mutation($id: ID!, $decision: String) {
     makeDecision(id: $id, decision: $decision) {
@@ -184,41 +170,6 @@ const makeDecisionMutation = gql`
     }
   }
 `
-
-// const updateCacheForFileCreation = (proxy, { data: { createFile } }) => {
-//   const data = proxy.readQuery({
-//     query,
-//     variables: {
-//       id: match.params.version,
-//     },
-//   })
-
-//   data.manuscript.reviews.map(review => {
-//     if (review.id === file.objectId) {
-//       review.comments.map(comment => {
-//         if (comment.type === createFile.fileType) {
-//           comment.files = [createFile]
-//         }
-//         return comment
-//       })
-//     }
-//     return review
-//   })
-
-//   proxy.writeQuery({ query, data })
-// }
-
-// const createFile = file => {
-
-//   mutate({
-//     variables: {
-//       file,
-//     },
-//     update:
-// },
-
-//
-
 const dateLabel = date => moment(date).format('YYYY-MM-DD')
 
 const decisionSections = ({
@@ -227,6 +178,9 @@ const decisionSections = ({
   isValid,
   updateReview,
   uploadFile,
+  isSubmitting,
+  submitCount,
+  dirty,
 }) => {
   const decisionSections = []
   const manuscriptVersions = manuscript.manuscriptVersions || []
@@ -263,8 +217,11 @@ const decisionSections = ({
         </AdminSection>
         <AdminSection key="decision-form">
           <DecisionForm
+            dirty={dirty}
             handleSubmit={handleSubmit}
+            isSubmitting={isSubmitting}
             isValid={isValid}
+            submitCount={submitCount}
             updateReview={updateReview}
             uploadFile={uploadFile}
           />
@@ -335,21 +292,33 @@ const decisionSections = ({
 
 const DecisionPage = ({ match }) => {
   // Hooks from the old world
-  const [makeDecision] = useMutation(makeDecisionMutation, {
-    // refetchQueries: [query],
-  })
+  const [makeDecision] = useMutation(makeDecisionMutation)
   const [updateReviewMutation] = useMutation(updateReviewMutationQuery)
 
-  // File upload
-  const [uploadReviewFiles] = useMutation(uploadReviewFilesMutation)
-
   const { loading, error, data } = useQuery(query, {
     variables: {
       id: match.params.version,
     },
-    fetchPolicy: 'network-only',
+    // fetchPolicy: 'cache-and-network',
   })
 
+  const reviewOrInitial = manuscript =>
+    (manuscript &&
+      manuscript.reviews &&
+      manuscript.reviews.find(review => review.isDecision)) || {
+      decisionComment: {},
+      isDecision: true,
+      recommendation: null,
+    }
+
+  // Find an existing review or create a placeholder, and hold a ref to it
+  const existingReview = useRef(reviewOrInitial(data?.manuscript))
+
+  // Update the value of that ref if the manuscript object changes
+  useEffect(() => {
+    existingReview.current = reviewOrInitial(data?.manuscript)
+  }, [data?.manuscript?.reviews])
+
   if (loading) return <Spinner />
   if (error) return `Error! ${error.message}`
 
@@ -361,41 +330,21 @@ const DecisionPage = ({ match }) => {
     channelId = manuscript.channels.find(c => c.type === 'editorial').id
   }
 
-  const uploadFile = (file, updateReview, type) =>
-    uploadReviewFiles({
-      variables: {
-        file,
-      },
-    }).then(({ data }) => {
-      // const newFile = {
-      //   url: data.upload.url,
-      //   filename: file.name,
-      //   size: file.size,
-      //   object: 'Review',
-      //   objectId: updateReview.id,
-      //   fileType: type,
-      // }
-      // createFile(newFile)
-    })
-
-  const updateReview = (data, file) => {
+  const updateReview = review => {
     const reviewData = {
-      isDecision: true,
+      recommendation: review.recommendation,
       manuscriptId: manuscript.id,
+      isDecision: true,
+      decisionComment: review.decisionComment && {
+        id: existingReview.current.decisionComment?.id,
+        commentType: 'decision',
+        content: review.decisionComment.content,
+      },
     }
 
-    if (data.comment) {
-      reviewData.comments = [data.comment]
-    }
-
-    if (data.recommendation) {
-      reviewData.recommendation = data.recommendation
-    }
-
-    const review = manuscript.reviews.find(review => review.isDecision) || {}
     return updateReviewMutation({
       variables: {
-        id: review.id || undefined,
+        id: existingReview.current.id || undefined,
         input: reviewData,
       },
       update: (cache, { data: { updateReview } }) => {
@@ -427,52 +376,52 @@ const DecisionPage = ({ match }) => {
       },
     })
   }
-
-  const initialValues = (manuscript.reviews &&
-    manuscript.reviews.find(review => review.isDecision)) || {
-    comments: [],
-    recommendation: null,
-  }
-
   // const editorSectionsResult = editorSections({ manuscript })
 
+  const sections = props =>
+    decisionSections({
+      manuscript,
+      handleSubmit: props.handleSubmit,
+      isValid: props.isValid,
+      updateReview,
+      isSubmitting: props.isSubmitting,
+      submitCount: props.submitCount,
+      dirty: props.dirty,
+    })
+
   return (
     <Columns>
       <Manuscript>
         <Formik
           displayName="decision"
-          initialValues={initialValues}
-          // isInitialValid={({ manuscript }) => {
-          //   const rv =
-          //     manuscript.reviews.find(review => review.isDecision) || {}
-          //   const isRecommendation = rv.recommendation != null
-          //   const isCommented = getCommentContent(rv, 'note') !== ''
-
-          //   return isCommented && isRecommendation
-          // }}
-          onSubmit={() => {
+          initialValues={reviewOrInitial(data.manuscript)}
+          onSubmit={values =>
             makeDecision({
               variables: {
                 id: manuscript.id,
-                decision: manuscript.reviews.find(review => review.isDecision)
-                  .recommendation,
+                decision: values.recommendation,
               },
             })
-          }}
+          }
           validate={(values, props) => {
             const errors = {}
-            if (getCommentContent(values, 'note') === '') {
-              errors.comments = 'Required'
+            if (
+              ['', '<p></p>', undefined].includes(
+                values.decisionComment?.content,
+              )
+            ) {
+              errors.decisionComment = 'Decision letter is required'
             }
 
             if (values.recommendation === null) {
-              errors.recommendation = 'Required'
+              errors.recommendation = 'Decision is required'
             }
             return errors
           }}
+          // validateOnMount
         >
           {props => (
-            // Temp
+            // TODO: Find a nicer way to display the contents of a manuscript
             <>
               {/* <Tabs
                 activeKey={
@@ -482,22 +431,8 @@ const DecisionPage = ({ match }) => {
                 title="Versions"
               /> */}
               <Tabs
-                activeKey={
-                  decisionSections({
-                    manuscript,
-                    handleSubmit: props.handleSubmit,
-                    isValid: props.isValid,
-                    updateReview,
-                    uploadFile,
-                  })[decisionSections.length - 1].key
-                }
-                sections={decisionSections({
-                  manuscript,
-                  handleSubmit: props.handleSubmit,
-                  isValid: props.isValid,
-                  updateReview,
-                  uploadFile,
-                })}
+                activeKey={sections(props)[decisionSections.length - 1].key}
+                sections={sections(props)}
                 title="Versions"
               />
             </>
diff --git a/app/components/component-review/src/components/ReviewPage.js b/app/components/component-review/src/components/ReviewPage.js
index d9c0e28f99c4af304912a4416abc3fd3bb734ec0..399c0a4ddcb60811387dd960e5265ce4450b9e68 100644
--- a/app/components/component-review/src/components/ReviewPage.js
+++ b/app/components/component-review/src/components/ReviewPage.js
@@ -1,30 +1,37 @@
-import React from 'react'
+import React, { useRef, useEffect } from 'react'
 import { useMutation, useQuery } from '@apollo/client'
 import gql from 'graphql-tag'
 import { Formik } from 'formik'
 // import { cloneDeep } from 'lodash'
-import { getCommentContent } from './review/util'
 import ReviewLayout from '../components/review/ReviewLayout'
 import { Spinner } from '../../../shared'
 import useCurrentUser from '../../../../hooks/useCurrentUser'
 
+const commentFields = `
+id
+commentType
+content
+files {
+  id
+  created
+  label
+  filename
+  fileType
+  mimeType
+  size
+  url
+}
+`
+
 const reviewFields = `
   id
   created
   updated
-  comments {
-    type
-    content
-    files {
-      id
-      created
-      label
-      filename
-      fileType
-      mimeType
-      size
-      url
-    }
+  reviewComment {
+    ${commentFields}
+  }
+  confidentialComment {
+    ${commentFields}
   }
   isDecision
   recommendation
@@ -34,23 +41,6 @@ const reviewFields = `
   }
 `
 
-// const teamFields = `
-//   id
-//   name
-//   role
-//   object {
-//     objectId
-//     objectType
-//   }
-//   members {
-//     id
-//     user {
-//       id
-//       username
-//     }
-//   }
-// `
-
 const fragmentFields = `
   id
   created
@@ -72,9 +62,8 @@ const fragmentFields = `
     id
     name
     role
-    object {
-      objectId
-      objectType
+    manuscript {
+      id
     }
     members {
       id
@@ -163,87 +152,39 @@ const updateReviewMutationQuery = gql`
   }
 `
 
-const uploadReviewFilesMutation = gql`
-  mutation($file: Upload!) {
-    upload(file: $file) {
-      url
-    }
-  }
-`
-
-const createFileMutation = gql`
-  mutation($file: Upload!) {
-    createFile(file: $file) {
-      id
-      created
-      label
-      filename
-      fileType
-      mimeType
-      size
-      url
-    }
-  }
-`
-
 export default ({ match, ...props }) => {
   const currentUser = useCurrentUser()
   const [updateReviewMutation] = useMutation(updateReviewMutationQuery)
   const [completeReview] = useMutation(completeReviewMutation)
 
-  // File upload
-  // const [uploadReviewFiles] = useMutation(uploadReviewFilesMutation)
-
-  const [createFileM] = useMutation(createFileMutation)
-  const createFile = file =>
-    createFileM({
-      variables: {
-        file,
-      },
-      update: (proxy, { data: { createFile } }) => {
-        const data = proxy.readQuery({
-          query,
-          variables: {
-            id: match.params.version,
-          },
-        })
-
-        data.manuscript.reviews.map(review => {
-          if (review.id === file.objectId) {
-            review.comments.map(comment => {
-              if (comment.type === createFile.fileType) {
-                comment.files = [createFile]
-              }
-              return comment
-            })
-          }
-          return review
-        })
-
-        proxy.writeQuery({ query, data })
-      },
-    })
-
   const { loading, error, data } = useQuery(query, {
     variables: {
       id: match.params.version,
     },
-    fetchPolicy: 'network-only',
   })
 
+  const reviewOrInitial = manuscript =>
+    (manuscript &&
+      manuscript.reviews &&
+      manuscript.reviews.find(
+        review => review?.user?.id === currentUser.id && !review.isDecision,
+      )) ||
+    {}
+
+  // Find an existing review or create a placeholder, and hold a ref to it
+  const existingReview = useRef(reviewOrInitial(data?.manuscript))
+
+  // Update the value of that ref if the manuscript object changes
+  useEffect(() => {
+    existingReview.current = reviewOrInitial(data?.manuscript)
+  }, [data?.manuscript?.reviews])
+
   if (loading) return <Spinner />
   if (error) return `Error! ${error.message}`
 
   const { manuscript } = data
   const channelId = manuscript.channels.find(c => c.type === 'editorial').id
 
-  const review =
-    (manuscript.reviews &&
-      manuscript.reviews.find(
-        review => review.user.id === currentUser.id && !review.isDecision,
-      )) ||
-    {}
-
   // eslint-disable-next-line
   const status = (
     (
@@ -253,61 +194,55 @@ export default ({ match, ...props }) => {
   ).status
 
   const updateReview = (review, file) => {
-    ;(review.comments || []).map(comment => {
-      delete comment.files
-      delete comment.__typename
-      return comment
-    })
-
     const reviewData = {
       recommendation: review.recommendation,
-      comments: review.comments,
       manuscriptId: manuscript.id,
+      reviewComment: review.reviewComment && {
+        id: existingReview.current.reviewComment?.id,
+        commentType: 'review',
+        content: review.reviewComment.content,
+      },
+      confidentialComment: review.confidentialComment && {
+        id: existingReview.current.confidentialComment?.id,
+        commentType: 'confidential',
+        content: review.confidentialComment.content,
+      },
     }
 
     return updateReviewMutation({
       variables: {
-        id: review.id || undefined,
+        id: existingReview.current.id || undefined,
         input: reviewData,
       },
-      // update: (proxy, { data: { updateReview } }) => {
-      //   const data = JSON.parse(
-      //     JSON.stringify(
-      //       proxy.readQuery({
-      //         query,
-      //         variables: {
-      //           id: manuscript.id,
-      //         },
-      //       }),
-      //     ),
-      //   )
-      //   let reviewIndex = data.manuscript.reviews.findIndex(
-      //     review => review.id === updateReview.id,
-      //   )
-      //   reviewIndex = reviewIndex < 0 ? 0 : reviewIndex
-      //   data.manuscript.reviews[reviewIndex] = updateReview
-      //   proxy.writeQuery({ query, data })
-      // },
-    })
-  }
+      update: (cache, { data: { updateReview } }) => {
+        cache.modify({
+          id: cache.identify(manuscript),
+          fields: {
+            reviews(existingReviewRefs = [], { readField }) {
+              const newReviewRef = cache.writeFragment({
+                data: updateReview,
+                fragment: gql`
+                  fragment NewReview on Review {
+                    id
+                  }
+                `,
+              })
+
+              if (
+                existingReviewRefs.some(
+                  ref => readField('id', ref) === updateReview.id,
+                )
+              ) {
+                return existingReviewRefs
+              }
 
-  const uploadFile = (file, updateReview, type) =>
-    uploadReviewFilesMutation({
-      variables: {
-        file,
+              return [...existingReviewRefs, newReviewRef]
+            },
+          },
+        })
       },
-    }).then(({ data }) => {
-      const newFile = {
-        url: data.upload.url,
-        filename: file.name,
-        mimeType: file.type,
-        size: file.size,
-        object: 'Review',
-        objectId: updateReview.id,
-        fileType: type,
-      }
-      createFile(newFile)
     })
+  }
 
   const handleSubmit = async ({ reviewId, history }) => {
     await completeReview({
@@ -324,7 +259,7 @@ export default ({ match, ...props }) => {
       initialValues={
         (manuscript.reviews &&
           manuscript.reviews.find(
-            review => review.user.id === currentUser.id && !review.isDecision,
+            review => review?.user?.id === currentUser.id && !review.isDecision,
           )) || {
           id: null,
           comments: [],
@@ -332,12 +267,15 @@ export default ({ match, ...props }) => {
         }
       }
       onSubmit={values =>
-        handleSubmit({ reviewId: review.id, history: props.history })
+        handleSubmit({
+          reviewId: existingReview.current.id,
+          history: props.history,
+        })
       }
       validateOnMount={review => {
         if (!review.id) return false
         const hasRecommendation = review.recommendation !== null
-        const comment = getCommentContent(review, 'note')
+        const comment = review.decisionComment?.content
         const isCommented = comment !== null && comment !== ''
 
         return isCommented && hasRecommendation
@@ -348,183 +286,12 @@ export default ({ match, ...props }) => {
           channelId={channelId}
           currentUser={currentUser}
           manuscript={manuscript}
-          review={review}
+          review={existingReview}
           status={status}
           updateReview={updateReview}
-          uploadFile={uploadFile}
           {...formikProps}
         />
       )}
     </Formik>
   )
 }
-
-// export default compose(
-// graphql(query, {
-//   options: ({ match }) => ({
-//     variables: {
-//       id: match.params.version,
-//     },
-//   }),
-// }),
-// graphql(uploadReviewFilesMutation, { name: 'uploadReviewFilesMutation' }),
-// graphql(updateReviewMutation, { name: 'updateReviewMutation' }),
-// graphql(updateTeam, { name: 'updateTeam' }),
-// graphql(createFileMutation, {
-//   props: ({ mutate, ownProps: { match } }) => ({
-//     createFile: file => {
-//       mutate({
-//         variables: {
-//           file,
-//         },
-//         update: (proxy, { data: { createFile } }) => {
-//           const data = proxy.readQuery({
-//             query,
-//             variables: {
-//               id: match.params.version,
-//             },
-//           })
-
-//           data.manuscript.reviews.map(review => {
-//             if (review.id === file.objectId) {
-//               review.comments.map(comment => {
-//                 if (comment.type === createFile.fileType) {
-//                   comment.files = [createFile]
-//                 }
-//                 return comment
-//               })
-//             }
-//             return review
-//           })
-
-//           proxy.writeQuery({ query, data })
-//         },
-//       })
-//     },
-//   }),
-// }),
-// withLoader(),
-// withProps(
-//   ({
-//     manuscript,
-//     currentUser,
-//     match: {
-//       params: { journal },
-//     },
-//     updateReviewMutation,
-//     uploadReviewFilesMutation,
-//     updateTeam,
-//     createFile,
-//   }) => ({
-// journal: { id: journal },
-// review:
-//   manuscript.reviews.find(
-//     review => review.user.id === currentUser.id && !review.isDecision,
-//   ) || {},
-// status: (
-//   (
-//     (manuscript.teams.find(team => team.role === 'reviewer') || {})
-//       .status || []
-//   ).find(status => status.user === currentUser.id) || {}
-// ).status,
-// updateReview: (review, file) => {
-//   ;(review.comments || []).map(comment => {
-//     delete comment.files
-//     delete comment.__typename
-//     return comment
-//   })
-
-//   const reviewData = {
-//     recommendation: review.recommendation,
-//     comments: review.comments,
-//     manuscriptId: manuscript.id,
-//   }
-
-//   return updateReviewMutation({
-//     variables: {
-//       id: review.id || undefined,
-//       input: reviewData,
-//     },
-//     update: (proxy, { data: { updateReview } }) => {
-//       const data = JSON.parse(
-//         JSON.stringify(
-//           proxy.readQuery({
-//             query,
-//             variables: {
-//               id: manuscript.id,
-//             },
-//           }),
-//         ),
-//       )
-//       let reviewIndex = data.manuscript.reviews.findIndex(
-//         review => review.id === updateReview.id,
-//       )
-//       reviewIndex = reviewIndex < 0 ? 0 : reviewIndex
-//       data.manuscript.reviews[reviewIndex] = updateReview
-//       proxy.writeQuery({ query, data })
-//     },
-//   })
-// },
-// uploadFile: (file, updateReview, type) =>
-//   uploadReviewFilesMutation({
-//     variables: {
-//       file,
-//     },
-//   }).then(({ data }) => {
-//     const newFile = {
-//       url: data.upload.url,
-//       filename: file.name,
-//       mimeType: file.type,
-//       size: file.size,
-//       object: 'Review',
-//       objectId: updateReview.id,
-//       fileType: type,
-//     }
-//     createFile(newFile)
-//   }),
-//     completeReview: history => {
-//       const team = cloneDeep(manuscript.teams).find(
-//         team => team.role === 'reviewer',
-//       )
-//       team.members = team.members.map(m => {
-//         if (m.user.id === currentUser.id) {
-//           return { user: { id: m.user.id }, status: 'completed' }
-//         }
-//         return { user: { id: m.user.id }, status: m.status }
-//       })
-
-//       updateTeam({
-//         variables: {
-//           id: team.id,
-//           input: {
-//             members: team.members,
-//           },
-//         },
-//       }).then(() => {
-//         history.push('/dashboard')
-//       })
-//     },
-//   }),
-// ),
-//   withFormik({
-//     mapPropsToValues: props =>
-//       props.manuscript.reviews.find(
-//         review => review.user.id === props.currentUser.id && !review.isDecision,
-//       ) || {
-//         id: null,
-//         comments: [],
-//         recommendation: null,
-//       },
-//     isInitialValid: ({ review }) => {
-//       if (!review.id) return false
-//       const hasRecommendation = review.recommendation !== null
-//       const comment = getCommentContent(review, 'note')
-//       const isCommented = comment !== null && comment !== ''
-
-//       return isCommented && hasRecommendation
-//     },
-//     displayName: 'review',
-//     handleSubmit: (props, { props: { completeReview, history } }) =>
-//       completeReview(history),
-//   }),
-// )(ReviewLayout)
diff --git a/app/components/component-review/src/components/ReviewersPage.js b/app/components/component-review/src/components/ReviewersPage.js
index 33ae162f81c2cde8fd1a89cf0aa5ee23a150687d..3c60c1b671a1ce4dc5a269eb26d5bb6d887d5860 100644
--- a/app/components/component-review/src/components/ReviewersPage.js
+++ b/app/components/component-review/src/components/ReviewersPage.js
@@ -9,9 +9,8 @@ const teamFields = `
   id
   role
   name
-  object {
-    objectId
-    objectType
+  manuscript {
+    id
   }
   members {
     id
@@ -46,17 +45,6 @@ const fragmentFields = `
     open
     recommendation
     created
-    comments {
-      type
-      content
-      files {
-        fileType
-        id
-        label
-        url
-        filename
-      }
-    }
     user {
       id
       username
@@ -114,8 +102,8 @@ const ReviewersPage = ({ match, history }) => {
     update: (cache, { data: { addReviewer } }) => {
       cache.modify({
         id: cache.identify({
-          __typename: addReviewer.object.objectType,
-          id: addReviewer.object.objectId,
+          __typename: 'Manuscript',
+          id: addReviewer.manuscript.id,
         }),
         fields: {
           teams(existingTeamRefs = []) {
diff --git a/app/components/component-review/src/components/assignEditors/AssignEditor.js b/app/components/component-review/src/components/assignEditors/AssignEditor.js
index f40d62c40715c1f6661e4ded7be794b2380b6714..d90c4328bc0ca22991ca52039ac5abbdfb945167 100644
--- a/app/components/component-review/src/components/assignEditors/AssignEditor.js
+++ b/app/components/component-review/src/components/assignEditors/AssignEditor.js
@@ -16,9 +16,8 @@ const teamFields = `
   id
   name
   role
-  object {
-    objectId
-    objectType
+  manuscript {
+    id
   }
   members {
     id
@@ -81,7 +80,7 @@ const AssignEditor = ({
       }
     }}
     options={options}
-    placeholder="Assign an editor…"
+    placeholder={`Assign ${teamName}…`}
     value={value}
   />
 )
@@ -113,8 +112,7 @@ export default compose(
     props: ({ mutate, ownProps }) => {
       const createTeam = (userId, teamRole) => {
         const input = {
-          objectId: ownProps.manuscript.id,
-          objectType: 'Manuscript',
+          manuscriptId: ownProps.manuscript.id,
           name:
             teamRole === 'seniorEditor' ? 'Senior Editor' : 'Handling Editor',
           role: teamRole,
diff --git a/app/components/component-review/src/components/decision/DecisionForm.js b/app/components/component-review/src/components/decision/DecisionForm.js
index fb0b035a64a8126e3ddad2903687eda9bfca2f46..cb5ae93c57495d6942b9ec80b9c01bcf1b3ece70 100644
--- a/app/components/component-review/src/components/decision/DecisionForm.js
+++ b/app/components/component-review/src/components/decision/DecisionForm.js
@@ -1,23 +1,19 @@
 import React, { useContext } from 'react'
 import { NoteEditor } from 'xpub-edit'
-import { cloneDeep, omit } from 'lodash'
-import { Field } from 'formik'
+// import { cloneDeep, omit } from 'lodash'
+import { Field, ErrorMessage } from 'formik'
 import {
   Button,
-  Flexbox,
+  // Flexbox,
   RadioGroup,
-  UploadButton,
-  UploadingFile,
+  // UploadButton,
+  // UploadingFile,
 } from '@pubsweet/ui'
 import { JournalContext } from '../../../../xpub-journal/src'
 import { required } from '../../../../xpub-validators/src'
+import { FilesUpload } from '../../../../shared'
 
-import {
-  getCommentFiles,
-  getCommentContent,
-  stripHtml,
-  createComments,
-} from '../review/util'
+import { reviewWithComment } from '../review/util'
 
 import {
   Container,
@@ -26,138 +22,158 @@ import {
   SectionRowGrid,
   SectionRow,
   SectionAction,
+  FormStatus,
+  ErrorText,
+  ErrorWrap,
 } from '../style'
 
-const NoteDecision = ({ updateReview, uploadFile }) => (
+// import Wax from '../../../../wax-collab/src/Editoria'
+
+const NoteDecision = ({ updateReview }) => (
   <>
-    <Field
-      component={NoteInput}
-      key="commentinput"
-      name="comments"
-      updateReview={updateReview}
-      validate={required}
-    />
-    <Field
-      component={AttachmentsInput('note')}
-      key="attachmentinput"
-      updateReview={updateReview}
-      uploadFile={uploadFile}
-    />
+    <Field key="noteField" name="decisionComment">
+      {formikBag => (
+        <>
+          <NoteInput updateReview={updateReview} {...formikBag} />
+          <FilesUpload
+            containerId={formikBag.field.value?.id}
+            containerName="reviewComment"
+            fieldName="decisionComment.files"
+            initializeContainer={async () => {
+              const review = reviewWithComment({
+                commentType: 'decision',
+                isDecision: true,
+                values: formikBag.form.values,
+                name: 'decisionComment',
+              })
+              const { data } = await updateReview(review)
+              return data.updateReview.decisionComment.id
+            }}
+          />
+        </>
+      )}
+    </Field>
   </>
 )
 
 const NoteInput = ({
   field,
-  form: { values, setFieldValue },
+  form: { errors, setFieldValue, setFieldTouched },
   updateReview,
 }) => (
-  <NoteEditor
-    key="note-input"
-    onBlur={() => {}}
-    onChange={value => {
-      const { updateIndex, comment } = createComments(
-        values,
-        {
-          type: 'note',
-          content: stripHtml(value),
-        },
-        'note',
-      )
-
-      setFieldValue(`comments.${updateIndex}`, comment)
-      updateReview(
-        cloneDeep(omit({ comment }, ['comment.files', 'comment.__typename'])),
-      )
-    }}
-    placeholder="Write/paste your decision letter here, or upload it using the upload button on the right."
-    value={getCommentContent({ comments: field.value }, 'note')}
-  />
-)
+  // const review = useState()
 
-const AttachmentsInput = type => ({
-  field,
-  form: { values, setFieldValue },
-  updateReview,
-  uploadFile,
-}) => (
-  <>
-    <UploadButton
-      buttonText="↑ Upload files"
-      key="note-attachment"
-      onChange={event => {
-        const val = event.target.files[0]
-        const file = cloneDeep(val)
-        file.filename = val.name
-        file.type = type
+  // const review = useRef({})
 
-        const { updateIndex, comment } = createComments(
-          field.value,
-          { files: [file] },
-          type,
-        )
+  // useEffect(() => {
+  //   review.current = reviewWithComment({
+  //     id: values.decisionComment?.id,
+  //     values,
+  //     commentType: 'decision',
+  //     name: 'decisionComment',
+  //     isDecision: true,
+  //   })
+  // }, [values])
 
-        setFieldValue(`comments.${updateIndex}.files`, comment.files)
+  // console.log('Rendering', review.current)
+  <ErrorWrap error={errors.decisionComment}>
+    {
+      // TODO: Use the best text editor there is!
+      /* <Wax
+      // fileUpload={fileUpload}
+      // onChange={source => updateManuscript({ source })}
+      content={field.value?.content}
+    /> */
+    }
 
-        updateReview({}).then(({ data: { updateReview } }) => {
-          uploadFile(val, updateReview, type)
+    <NoteEditor
+      data-testid="decisionComment"
+      debounceDelay={300}
+      key="note-input"
+      onBlur={() => setFieldTouched('decisionComment')}
+      onChange={value => {
+        setFieldValue('decisionComment', { content: value })
+        updateReview({
+          decisionComment: { content: value },
         })
       }}
+      placeholder="Write/paste your decision letter here, or upload it using the upload button on the right."
+      value={field.value?.content || ''}
     />
-    <Flexbox>
-      {getCommentFiles(field.value, 'note').map(val => {
-        const file = cloneDeep(val)
-        file.name = file.filename
-        return <UploadingFile file={file} key={file.name} uploaded />
-      })}
-    </Flexbox>
-  </>
+    <ErrorText>
+      <ErrorMessage name="decisionComment" />
+    </ErrorText>
+  </ErrorWrap>
 )
 
 const RecommendationInput = ({
   field,
-  form: { setFieldValue },
+  form: { setFieldValue, errors },
   updateReview,
 }) => {
   const journal = useContext(JournalContext)
   return (
-    <RadioGroup
-      {...field}
-      inline
-      onChange={val => {
-        setFieldValue(`recommendation`, val)
-        updateReview({ recommendation: val })
-      }}
-      options={journal.recommendations}
-      value={field.value === '' ? null : field.value}
-    />
+    <div>
+      <RadioGroup
+        {...field}
+        inline
+        onChange={val => {
+          setFieldValue('recommendation', val)
+          updateReview({ recommendation: val })
+        }}
+        options={journal.recommendations}
+        value={field.value === '' ? null : field.value}
+      />
+      <ErrorMessage name="recommendation" />
+    </div>
   )
 }
 
-const DecisionForm = ({ handleSubmit, uploadFile, updateReview, isValid }) => (
-  <Container key="decisionform">
-    <form onSubmit={handleSubmit}>
-      <SectionHeader>
-        <Title>Decision</Title>
-      </SectionHeader>
-      <SectionRow key="note">
-        <NoteDecision updateReview={updateReview} uploadFile={uploadFile} />
-      </SectionRow>
-      <SectionRowGrid>
-        <Field
-          component={RecommendationInput}
-          name="recommendation"
-          updateReview={updateReview}
-          validate={required}
-        />
+const DecisionForm = ({
+  handleSubmit,
+  updateReview,
+  isValid,
+  isSubmitting,
+  submitCount,
+  dirty,
+}) => {
+  let status = null
+  if (isSubmitting) {
+    status = 'Your decision is submitting...'
+  } else if (submitCount) {
+    status = 'Your decision has been saved.'
+  }
 
-        <SectionAction key="submit">
-          <Button disabled={!isValid} primary type="submit">
-            Submit
-          </Button>
-        </SectionAction>
-      </SectionRowGrid>
-    </form>
-  </Container>
-)
+  return (
+    <Container key="decisionform">
+      <form onSubmit={handleSubmit}>
+        <SectionHeader>
+          <Title>Decision</Title>
+        </SectionHeader>
+        <SectionRow key="note">
+          <NoteDecision updateReview={updateReview} />
+        </SectionRow>
+        <SectionRowGrid>
+          <Field
+            component={RecommendationInput}
+            name="recommendation"
+            updateReview={updateReview}
+            validate={required}
+          />
+          <FormStatus>{status}</FormStatus>
+          <SectionAction key="submit">
+            <Button
+              disabled={!isValid || isSubmitting || !dirty}
+              primary
+              type="submit"
+            >
+              Submit
+            </Button>
+          </SectionAction>
+        </SectionRowGrid>
+      </form>
+    </Container>
+  )
+}
 
 export default DecisionForm
diff --git a/app/components/component-review/src/components/decision/DecisionReviews.js b/app/components/component-review/src/components/decision/DecisionReviews.js
index b7bcab3bd4252b71f56f752800d75ed56aff5d5e..5e689ae81834c98f89f746345cc97d54515fbc88 100644
--- a/app/components/component-review/src/components/decision/DecisionReviews.js
+++ b/app/components/component-review/src/components/decision/DecisionReviews.js
@@ -15,7 +15,7 @@ const getCompletedReviews = (manuscript, currentUser) => {
   if (!team.members) {
     return null
   }
-  const currentMember = team.members.find(m => m.user.id === currentUser.id)
+  const currentMember = team.members.find(m => m.user?.id === currentUser?.id)
   return currentMember && currentMember.status
 }
 
diff --git a/app/components/component-review/src/components/review/Review.js b/app/components/component-review/src/components/review/Review.js
index e8e3c65dc6850e54f3fdc01d13de77d0ea4ce077..596489beebfb1a7bb5c1a1d18f3e7f9726602a72 100644
--- a/app/components/component-review/src/components/review/Review.js
+++ b/app/components/component-review/src/components/review/Review.js
@@ -3,7 +3,6 @@ import styled from 'styled-components'
 import { NoteViewer } from 'xpub-edit'
 import { Attachment } from '@pubsweet/ui'
 import { th, grid } from '@pubsweet/ui-toolkit'
-import { getCommentFiles } from './util'
 
 const Heading = styled.div``
 const Note = styled.div`
@@ -23,17 +22,12 @@ const filesToAttachment = file => ({
   url: file.url,
 })
 
-const findComments = (review = {}, type) => {
-  const comments = review.comments || []
-  return comments.find(comment => comment.type === type)
-}
-
 const ReviewComments = (review, type) => (
   <Note>
     <Content>
-      <NoteViewer value={findComments(review, type).content} />
+      <NoteViewer value={review[`${type}Comment`].content} />
     </Content>
-    {getCommentFiles(review, type).map(attachment => (
+    {review[`${type}Comment`].files.map(attachment => (
       <Attachment
         file={filesToAttachment(attachment)}
         key={attachment.url}
@@ -45,14 +39,14 @@ const ReviewComments = (review, type) => (
 
 const Review = ({ review }) => (
   <Container>
-    {findComments(review, 'note') && (
+    {review.reviewComment && (
       <div>
-        <Heading>Note</Heading>
+        <Heading>Review</Heading>
 
-        {ReviewComments(review, 'note')}
+        {ReviewComments(review, 'review')}
       </div>
     )}
-    {findComments(review, 'confidential') && (
+    {review.confidentialComment && (
       <div>
         <Heading>Confidential</Heading>
 
diff --git a/app/components/component-review/src/components/review/ReviewForm.js b/app/components/component-review/src/components/review/ReviewForm.js
index 006bf2dbef454c685e951e185facc6e5a2628746..d68150650124824d4a09169ca4ffb1ae48efa03d 100644
--- a/app/components/component-review/src/components/review/ReviewForm.js
+++ b/app/components/component-review/src/components/review/ReviewForm.js
@@ -1,17 +1,10 @@
 import React, { useContext } from 'react'
-import { cloneDeep, set } from 'lodash'
 import { Field } from 'formik'
 import { NoteEditor } from 'xpub-edit'
-import {
-  Button,
-  Flexbox,
-  RadioGroup,
-  UploadButton,
-  UploadingFile,
-} from '@pubsweet/ui'
+import { Button, RadioGroup } from '@pubsweet/ui'
 
 import { JournalContext } from '../../../../xpub-journal/src'
-import { getCommentFiles, stripHtml, createComments } from './util'
+import { reviewWithComment } from './util'
 import {
   AdminSection,
   Container,
@@ -21,46 +14,47 @@ import {
   SectionRow,
   SectionAction,
 } from '../style'
+import { FilesUpload } from '../../../../shared'
 
-const AttachmentsInput = ({
-  field,
-  form: { values },
-  updateReview,
-  uploadFile,
-  type,
-}) => (
-  <>
-    <UploadButton
-      buttonText="↑ Upload files"
-      onChange={event => {
-        const val = event.target.files[0]
-        const file = cloneDeep(val)
-        file.filename = val.name
-        file.type = type
+// const AttachmentsInput = ({
+//   field,
+//   form: { values },
+//   updateReview,
+//   uploadFile,
+//   type,
+// }) => (
+//   <>
+//     <UploadButton
+//       buttonText="↑ Upload files"
+//       onChange={event => {
+//         const val = event.target.files[0]
+//         const file = cloneDeep(val)
+//         file.filename = val.name
+//         file.type = type
 
-        const { updateIndex, comment } = createComments(
-          values,
-          { files: [file] },
-          type,
-        )
+//         const { updateIndex, comment } = createComments(
+//           values,
+//           { files: [file] },
+//           type,
+//         )
 
-        const data = cloneDeep(values)
-        set(data, `comments.${updateIndex}`, comment)
+//         const data = cloneDeep(values)
+//         set(data, `comments.${updateIndex}`, comment)
 
-        updateReview(data).then(({ data: { updateReview } }) => {
-          uploadFile(val, updateReview, type)
-        })
-      }}
-    />
-    <Flexbox>
-      {getCommentFiles(values, type).map(val => {
-        const file = cloneDeep(val)
-        file.name = file.filename
-        return <UploadingFile file={file} key={file.name} uploaded />
-      })}
-    </Flexbox>
-  </>
-)
+//         updateReview(data).then(({ data: { updateReview } }) => {
+//           uploadFile(val, updateReview, type)
+//         })
+//       }}
+//     />
+//     <Flexbox>
+//       {getCommentFiles(values, type).map(val => {
+//         const file = cloneDeep(val)
+//         file.name = file.filename
+//         return <UploadingFile file={file} key={file.name} uploaded />
+//       })}
+//     </Flexbox>
+//   </>
+// )
 
 const NoteInput = ({
   field,
@@ -69,26 +63,22 @@ const NoteInput = ({
   ...rest
 }) => (
   <NoteEditor
+    data-testid="reviewComment"
     key="note-comment"
     placeholder="Enter your review…"
     title="Comments to the Author"
     {...field}
     onBlur={value => {
-      const { comment } = createComments(
-        values,
-        {
-          type: 'note',
-          content: stripHtml(value),
-        },
-        'note',
-      )
-
-      setFieldValue(`comments.0`, comment)
-      const data = cloneDeep(values)
-      set(data, `comments.0`, comment)
-      updateReview(data)
+      // const review = reviewWithComment({
+      //   id: values.reviewComment?.id,
+      //   value,
+      //   values,
+      //   commentType: 'review',
+      //   name: 'reviewComment',
+      // })
+      updateReview({ reviewComment: { content: value } })
     }}
-    value={field.value || ''}
+    value={field.value?.content || ''}
   />
 )
 
@@ -98,26 +88,22 @@ const ConfidentialInput = ({
   updateReview,
 }) => (
   <NoteEditor
+    data-testid="confidentialComment"
     key="confidential-comment"
     placeholder="Enter a confidential note to the editor (optional)…"
     title="Confidential Comments to Editor (Optional)"
     {...field}
     onBlur={value => {
-      const { comment } = createComments(
-        values,
-        {
-          type: 'confidential',
-          content: stripHtml(value),
-        },
-        'confidential',
-      )
-
-      setFieldValue(`comments.1`, comment)
-      const data = cloneDeep(values)
-      set(data, `comments.1`, comment)
-      updateReview(data)
+      // const review = reviewWithComment({
+      //   id: values.confidentialComment?.id,
+      //   value,
+      //   values,
+      //   commentType: 'confidential',
+      //   name: 'confidentialComment',
+      // })
+      updateReview({ confidentialComment: { content: value } })
     }}
-    value={field.value || ''}
+    value={field.value?.content || ''}
   />
 )
 
@@ -127,39 +113,76 @@ const RecommendationInput = ({ field, form: { values }, updateReview }) => {
     <RadioGroup
       inline
       {...field}
+      data-testid="recommendation"
       onChange={val => {
-        const data = cloneDeep(values)
-        set(data, 'recommendation', val)
-        updateReview(data)
+        updateReview({ recommendation: val })
       }}
       options={journal.recommendations}
     />
   )
 }
-const ReviewComment = props => (
+const ReviewComment = ({ updateReview }) => (
   <>
     <AdminSection>
       <div name="note">
-        <Field key="noteField" name="comments.0.content">
-          {extraProps => <NoteInput {...props} {...extraProps} />}
-        </Field>
-        <Field
-          component={extraProps => (
-            <AttachmentsInput type="note" {...props} {...extraProps} />
+        <Field key="noteField" name="reviewComment">
+          {formikBag => (
+            <>
+              <NoteInput updateReview={updateReview} {...formikBag} />
+              <FilesUpload
+                containerId={formikBag.field.value?.id}
+                containerName="reviewComment"
+                fieldName="reviewComment.files"
+                initializeContainer={async () => {
+                  // If the container for the uploaded files is not present,
+                  // we have to create it. InitializeContainer will be called
+                  // if containerId is undefined
+                  const review = reviewWithComment({
+                    commentType: 'review',
+                    values: formikBag.form.values,
+                    name: 'reviewComment',
+                  })
+                  // This is an upsert
+                  const { data } = await updateReview(review)
+                  // And we the return the file container id, so
+                  // that we have somewhere to attach uploaded files
+                  return data.updateReview.reviewComment.id
+                }}
+              />
+            </>
           )}
-        />
+        </Field>
       </div>
     </AdminSection>
     <AdminSection>
       <div name="confidential">
-        <Field key="confidentialField" name="comments.1.content">
-          {extraProps => <ConfidentialInput {...props} {...extraProps} />}
-        </Field>
-        <Field
-          component={extraProps => (
-            <AttachmentsInput type="confidential" {...props} {...extraProps} />
+        <Field key="confidentialField" name="confidentialComment">
+          {formikBag => (
+            <>
+              <ConfidentialInput updateReview={updateReview} {...formikBag} />
+              <FilesUpload
+                containerId={formikBag.field.value?.id}
+                containerName="reviewComment"
+                fieldName="confidentialComment.files"
+                initializeContainer={async () => {
+                  // If the container for the uploaded files is not present,
+                  // we have to create it. InitializeContainer will be called
+                  // if containerId is undefined
+                  const review = reviewWithComment({
+                    commentType: 'confidential',
+                    values: formikBag.form.values,
+                    name: 'confidentialComment',
+                  })
+                  // This is an upsert
+                  const { data } = await updateReview(review)
+                  // And we the return the file container id, so
+                  // that we have somewhere to attach uploaded files
+                  return data.updateReview.confidentialComment.id
+                }}
+              />
+            </>
           )}
-        />
+        </Field>
       </div>
     </AdminSection>
   </>
@@ -181,11 +204,7 @@ const ReviewForm = ({
           <Title>Review</Title>
         </SectionHeader>
         <SectionRow key="note">
-          <ReviewComment
-            review={review}
-            updateReview={updateReview}
-            uploadFile={uploadFile}
-          />
+          <ReviewComment review={review} updateReview={updateReview} />
         </SectionRow>
         <SectionHeader>
           <Title>Recommendation</Title>
diff --git a/app/components/component-review/src/components/review/util.js b/app/components/component-review/src/components/review/util.js
index a760b1ba6da04d9e430ba7cbd223e6fe334d8364..b382a482636feb3f3e950e37866c7410aac0dd5c 100644
--- a/app/components/component-review/src/components/review/util.js
+++ b/app/components/component-review/src/components/review/util.js
@@ -4,35 +4,21 @@ export const stripHtml = htmlString => {
   return temp.textContent
 }
 
-export const getCommentFiles = (review = {}, type) => {
-  const comments =
-    (review.comments || []).find(comment => (comment || {}).type === type) || {}
-  return comments.files || []
-}
-
-export const getCommentContent = (review = {}, type) => {
-  const comments =
-    (review.comments || []).find(comment => (comment || {}).type === type) || {}
-  return comments.content || ''
-}
-
-export const createComments = (values, val, type) => {
-  let updateIndex = (values.comments || []).findIndex(
-    comment => (comment || {}).type === type,
-  )
-  updateIndex =
-    (values.comments || []).length > 0 && updateIndex < 0 ? 1 : updateIndex
-  updateIndex = updateIndex < 0 ? 0 : updateIndex
-
-  const comment = Object.assign(
-    {
-      type,
-      content: '',
-      files: [],
-    },
-    (values.comments || [])[updateIndex],
-    val,
-  )
+export const reviewWithComment = ({
+  id,
+  value,
+  values,
+  commentType,
+  name,
+  isDecision,
+}) => {
+  const data = { id: values.id }
 
-  return { updateIndex, comment }
+  data.isDecision = isDecision
+  data[name] = {
+    id,
+    commentType,
+    content: value ? stripHtml(value) : '',
+  }
+  return data
 }
diff --git a/app/components/component-review/src/components/reviewers/Reviewers.js b/app/components/component-review/src/components/reviewers/Reviewers.js
index 7935205aaf4f36260233d9db89b11d8d6df84bec..5ff9ad3024d1df2a45274171f193e96e9f320f56 100644
--- a/app/components/component-review/src/components/reviewers/Reviewers.js
+++ b/app/components/component-review/src/components/reviewers/Reviewers.js
@@ -43,9 +43,7 @@ const Reviewers = ({
       <Heading>Reviewers</Heading>
       <Button
         onClick={() =>
-          history.push(
-            `/journal/versions/${manuscript.id}/decisions/${manuscript.id}`,
-          )
+          history.push(`/journal/versions/${manuscript.id}/decision`)
         }
         primary
       >
diff --git a/app/components/component-review/src/components/style.js b/app/components/component-review/src/components/style.js
index c4b49a22d76b59e398ad3565f80a04fcd111dfa7..97ac3ec29fa133d9479466d57424f991254997c8 100644
--- a/app/components/component-review/src/components/style.js
+++ b/app/components/component-review/src/components/style.js
@@ -1,4 +1,4 @@
-import styled from 'styled-components'
+import styled, { css } from 'styled-components'
 import { th, grid } from '@pubsweet/ui-toolkit'
 
 export const Columns = styled.div`
@@ -77,6 +77,36 @@ export const Container = styled.div`
   }
 `
 
+export const FormStatus = styled.div`
+  line-height: ${grid(5)};
+  text-align: center;
+  color: ${th('colorSecondary')};
+`
+
+export const ErrorWrap = styled.div`
+  .ProseMirror {
+    margin-bottom: ${grid(4)};
+  }
+  ${({ error }) =>
+    error &&
+    css`
+      .ProseMirror {
+        border-color: red;
+      }
+      ${ErrorText} {
+        margin-top: ${grid(-4)};
+        margin-bottom: ${grid(1)};
+      }
+    `}
+
+  [class*="MenuBar"] {
+    margin-top: 0;
+  }
+`
+export const ErrorText = styled.div`
+  color: red;
+`
+
 export {
   Title,
   SectionHeader,
diff --git a/app/components/component-submit/src/components/FormTemplate.js b/app/components/component-submit/src/components/FormTemplate.js
index 092be22b20b10b94f065ae32c2e9b364f37a0317..f23d8e4ee663a89668791247bbb69b054c8be5c2 100644
--- a/app/components/component-submit/src/components/FormTemplate.js
+++ b/app/components/component-submit/src/components/FormTemplate.js
@@ -12,10 +12,9 @@ import {
 } from '@pubsweet/ui'
 import * as validators from 'xpub-validators'
 import { AbstractEditor } from 'xpub-edit'
-import { Section as Container, Select } from '../../../shared'
+import { Section as Container, Select, FilesUpload } from '../../../shared'
 import { Heading1, Section, Legend, SubNote } from '../style'
 import AuthorsInput from './AuthorsInput'
-import Supplementary from './Supplementary'
 import Confirm from './Confirm'
 
 // TODO: https://github.com/formium/formik/issues/146#issuecomment-474775723
@@ -267,8 +266,10 @@ export default ({
             {/* <p>{JSON.stringify(element)}</p> */}
             <Legend dangerouslySetInnerHTML={createMarkup(element.title)} />
             {element.component === 'SupplementaryFiles' && (
-              <Supplementary
-                createSupplementaryFile={createSupplementaryFile}
+              <FilesUpload
+                containerId={manuscript.id}
+                containerName="manuscript"
+                fileType="supplementary"
                 onChange={onChange}
               />
             )}
diff --git a/app/components/component-submit/src/components/SubmitPage.js b/app/components/component-submit/src/components/SubmitPage.js
index c510504b92d171e1d410ae6b3df910f63d339f59..4da4c390a62708307b6e50156851e2e87826c011 100644
--- a/app/components/component-submit/src/components/SubmitPage.js
+++ b/app/components/component-submit/src/components/SubmitPage.js
@@ -25,9 +25,6 @@ const fragmentFields = `
     recommendation
     created
     isDecision
-    comments {
-      content
-    }
     user {
       id
       username
@@ -125,21 +122,6 @@ const updateMutation = gql`
 //   }
 // `
 
-const createFileMutation = gql`
-  mutation($file: Upload!, $meta: FileMetaInput) {
-    createFile(file: $file, meta: $meta) {
-      id
-      created
-      label
-      filename
-      fileType
-      mimeType
-      size
-      url
-    }
-  }
-`
-
 const SubmitPage = ({ match, history, ...props }) => {
   const [confirming, setConfirming] = useState(false)
 
@@ -151,26 +133,6 @@ const SubmitPage = ({ match, history, ...props }) => {
     variables: { id: match.params.version, form: 'submit' },
   })
 
-  const [createFile] = useMutation(createFileMutation)
-
-  const createSupplementaryFile = file => {
-    const meta = {
-      filename: file.name,
-      mimeType: file.type,
-      size: file.size,
-      fileType: 'supplementary',
-      object: 'Manuscript',
-      objectId: match.params.version,
-    }
-
-    createFile({
-      variables: {
-        file,
-        meta,
-      },
-    })
-  }
-
   const [update] = useMutation(updateMutation)
 
   if (loading) return <Spinner />
@@ -226,7 +188,6 @@ const SubmitPage = ({ match, history, ...props }) => {
       {props => (
         <Submit
           confirming={confirming}
-          createSupplementaryFile={createSupplementaryFile}
           forms={cloneDeep(getFile)}
           manuscript={manuscript}
           onChange={handleChange}
diff --git a/app/components/component-submit/src/components/Supplementary.js b/app/components/component-submit/src/components/Supplementary.js
deleted file mode 100644
index 0432c9488da0896b8e7e21522fc7629516d82cc2..0000000000000000000000000000000000000000
--- a/app/components/component-submit/src/components/Supplementary.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import React from 'react'
-import { cloneDeep } from 'lodash'
-import { FieldArray } from 'formik'
-import { Flexbox, UploadButton, UploadingFile } from '@pubsweet/ui'
-
-const renderFilesUpload = (onChange, createSupplementaryFile) => ({
-  form: { values, setFieldValue },
-  push,
-  insert,
-}) => (
-  <>
-    <UploadButton
-      buttonText="↑ Upload files"
-      onChange={event => {
-        const fileArray = Array.from(event.target.files).map(file => {
-          const fileUpload = {
-            fileType: 'supplementary',
-            filename: file.name,
-          }
-          return fileUpload
-        })
-        setFieldValue('files', fileArray.concat(values.files))
-        Array.from(event.target.files).forEach(file => {
-          createSupplementaryFile(file)
-        })
-      }}
-    />
-    <Flexbox>
-      {cloneDeep(values.files || [])
-        .filter(val => val.fileType === 'supplementary')
-        .map(val => {
-          val.name = val.filename
-          return <UploadingFile file={val} key={val.name} uploaded />
-        })}
-    </Flexbox>
-  </>
-)
-
-const Supplementary = ({ onChange, createSupplementaryFile }) => (
-  <FieldArray
-    name="files"
-    render={renderFilesUpload(onChange, createSupplementaryFile)}
-  />
-)
-
-export default Supplementary
diff --git a/app/components/component-submit/src/components/UploadButton.js b/app/components/component-submit/src/components/UploadButton.js
new file mode 100644
index 0000000000000000000000000000000000000000..fb6c96449dceff7ad115457742617e4072406a7d
--- /dev/null
+++ b/app/components/component-submit/src/components/UploadButton.js
@@ -0,0 +1,33 @@
+import React from 'react'
+import styled from 'styled-components'
+import { th } from '@pubsweet/ui-toolkit'
+
+const Button = styled.button.attrs(() => ({
+  type: 'button',
+}))`
+  background: transparent;
+  border: ${th('borderWidth')} dashed ${th('colorBorder')};
+  height: calc(${th('gridUnit')} * 3);
+  cursor: pointer;
+  margin-bottom: calc(${th('gridUnit')} * 3);
+  padding: ${th('gridUnit')};
+`
+
+const UploadButton = ({ name, buttonText, onChange }) => {
+  let fileInput
+  return (
+    <React.Fragment>
+      <Button onClick={() => fileInput.click()}>{buttonText}</Button>
+      <input
+        multiple
+        name={name}
+        onChange={onChange}
+        ref={input => (fileInput = input)}
+        style={{ display: 'none' }}
+        type="file"
+      />
+    </React.Fragment>
+  )
+}
+
+export default UploadButton
diff --git a/app/components/component-submit/src/components/UploadManuscript.js b/app/components/component-submit/src/components/UploadManuscript.js
index cdb5e7e0bed5cceb05818b51c8313b0736ebd587..807e004c4350ab54642dd32e46261722fb931738 100644
--- a/app/components/component-submit/src/components/UploadManuscript.js
+++ b/app/components/component-submit/src/components/UploadManuscript.js
@@ -2,17 +2,9 @@ import React, { useContext } from 'react'
 import styled, { keyframes, withTheme } from 'styled-components'
 import { Icon, Action } from '@pubsweet/ui'
 import { th } from '@pubsweet/ui-toolkit'
-import Dropzone from 'react-dropzone'
 import { XpubContext } from '../../../xpub-with-context/src'
 import upload from '../upload'
-
-const StyledDropzone = styled(({ disableUpload, ...props }) => (
-  <Dropzone {...props} />
-))`
-  border: none;
-  cursor: pointer;
-  ${({ disableUpload }) => disableUpload && 'pointer-events: none;'};
-`
+import { Dropzone } from '../../../shared'
 
 const StatusIcon = withTheme(({ children, theme }) => (
   <Icon color={theme.colorPrimary}>{children}</Icon>
@@ -188,7 +180,7 @@ const UploadManuscript = ({ acceptFiles, ...props }) => {
 
   return (
     <>
-      <StyledDropzone
+      <Dropzone
         accept={acceptFiles}
         data-testid="dropzone"
         disableUpload={converting ? 'disableUpload' : null}
@@ -222,7 +214,7 @@ const UploadManuscript = ({ acceptFiles, ...props }) => {
             </SubInfo>
           </Root>
         )}
-      </StyledDropzone>
+      </Dropzone>
       <Action onClick={() => uploadManuscript()}>Submit a URL instead</Action>
     </>
   )
diff --git a/app/components/component-submit/src/upload.js b/app/components/component-submit/src/upload.js
index 3a733062e9901182411a6d9212f342a7b835814f..2a33bc2cddca6daa657d46249bbb2e2d3e43754a 100644
--- a/app/components/component-submit/src/upload.js
+++ b/app/components/component-submit/src/upload.js
@@ -1,6 +1,7 @@
 import config from 'config'
 import request from 'pubsweet-client/src/helpers/api'
 import gql from 'graphql-tag'
+import currentRolesVar from '../../../shared/currentRolesVar'
 
 const generateTitle = name =>
   name
@@ -35,9 +36,8 @@ const createManuscriptMutation = gql`
         id
         role
         name
-        object {
-          objectId
-          objectType
+        manuscript {
+          id
         }
         members {
           id
@@ -154,6 +154,11 @@ const createManuscriptPromise = (
     mutation: createManuscriptMutation,
     variables: { input: manuscript },
     update: (cache, { data: { createManuscript } }) => {
+      const currentRoles = currentRolesVar()
+      currentRolesVar([
+        ...currentRoles,
+        { id: createManuscript.id, roles: ['author'] },
+      ])
       cache.modify({
         fields: {
           manuscripts(existingManuscriptRefs = []) {
diff --git a/app/components/component-teams-manager/src/components/Team.jsx b/app/components/component-teams-manager/src/components/Team.jsx
deleted file mode 100644
index 66535822dfae39ab2d1e612b7141ca26b1721575..0000000000000000000000000000000000000000
--- a/app/components/component-teams-manager/src/components/Team.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React from 'react'
-import styled from 'styled-components'
-import { Button, Menu } from '@pubsweet/ui'
-import { TeamTableCell } from './molecules/Table'
-
-const StyledMenu = styled(Menu)`
-  width: 100%;
-`
-
-const Team = ({ team, number, userOptions, deleteTeam, updateTeam }) => (
-  <>
-    <TeamTableCell width={5}>{number}</TeamTableCell>
-    <TeamTableCell>
-      {team.name} {team.role}
-    </TeamTableCell>
-    <TeamTableCell>
-      {team.object && team.object.objectType}{' '}
-      {team.object && team.object.objectId}
-    </TeamTableCell>
-    <TeamTableCell width={40}>
-      <StyledMenu
-        inline
-        multi
-        name="members"
-        onChange={members => updateTeam(members, team)}
-        options={userOptions}
-        value={team.members.map(member => member.user && member.user.id)}
-      />
-    </TeamTableCell>
-    <TeamTableCell width={15}>
-      <Button onClick={() => deleteTeam(team)}>Delete</Button>
-    </TeamTableCell>
-  </>
-)
-
-export default Team
diff --git a/app/components/component-teams-manager/src/components/TeamCreator.jsx b/app/components/component-teams-manager/src/components/TeamCreator.jsx
deleted file mode 100644
index 1fb0aab0d6860db8017acd57dfbd3644882ebf10..0000000000000000000000000000000000000000
--- a/app/components/component-teams-manager/src/components/TeamCreator.jsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import React from 'react'
-import { find } from 'lodash'
-import { compose, withHandlers, withState } from 'recompose'
-import { Button, Menu } from '@pubsweet/ui'
-
-const TeamCreator = ({
-  teamTypeSelected,
-  manuscriptSelected,
-  manuscriptsOptions,
-  typesOptions,
-  onChangeManuscript,
-  onChangeType,
-  onSave,
-}) => (
-  <form onSubmit={onSave}>
-    <h3>Create a new team</h3>
-    <h4>Team type</h4>
-    <Menu
-      name="teamType"
-      onChange={onChangeType}
-      options={typesOptions}
-      required
-      reset={teamTypeSelected}
-      value={teamTypeSelected}
-    />
-    <h4>Manuscript</h4>
-    <Menu
-      name="collection"
-      onChange={onChangeManuscript}
-      options={manuscriptsOptions}
-      required
-      reset={manuscriptSelected}
-      value={manuscriptSelected}
-    />
-    <Button primary type="submit">
-      Create
-    </Button>
-  </form>
-)
-
-export default compose(
-  withState('manuscriptSelected', 'onManuscriptSelect', false),
-  withState('teamTypeSelected', 'onTeamTypeSelect', false),
-  withHandlers({
-    onChangeManuscript: ({ onManuscriptSelect }) => collectionId =>
-      onManuscriptSelect(() => collectionId || false),
-    onChangeType: ({ onTeamTypeSelect }) => teamType =>
-      onTeamTypeSelect(() => teamType || false),
-    onSave: ({
-      teamTypeSelected,
-      manuscriptSelected,
-      create,
-      typesOptions,
-      onTeamTypeSelect,
-      onManuscriptSelect,
-    }) => event => {
-      event.preventDefault()
-      const role = teamTypeSelected
-
-      let objectId
-      let objectType
-
-      if (manuscriptSelected) {
-        objectId = manuscriptSelected
-        objectType = 'Manuscript'
-      }
-
-      if (role && objectId && objectType) {
-        create({
-          name: find(typesOptions, types => types.value === role).label,
-          role,
-          objectId,
-          objectType,
-          members: [],
-        })
-
-        onTeamTypeSelect(() => true)
-        onManuscriptSelect(() => true)
-      }
-    },
-  }),
-)(TeamCreator)
diff --git a/app/components/component-teams-manager/src/components/TeamsManager.jsx b/app/components/component-teams-manager/src/components/TeamsManager.jsx
deleted file mode 100644
index a7e75164e88770b167da1b3bd52e8e7ecea3009b..0000000000000000000000000000000000000000
--- a/app/components/component-teams-manager/src/components/TeamsManager.jsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import React from 'react'
-import { Page } from './molecules/Page'
-import { TeamTableCell, TeamTable } from './molecules/Table'
-import Team from './Team'
-import TeamCreator from './TeamCreator'
-
-const TeamsManager = ({
-  teams = [],
-  updateTeam,
-  deleteTeam,
-  createTeam,
-  error,
-  userOptions,
-  manuscriptsOptions,
-  typesOptions,
-}) => (
-  <Page>
-    {error ? <div>{error}</div> : null}
-    {teams.length > 0 && (
-      <TeamTable>
-        <TeamTableCell width={5}>#</TeamTableCell>
-        <TeamTableCell>Type</TeamTableCell>
-        <TeamTableCell>Object</TeamTableCell>
-        <TeamTableCell width={40}>Members</TeamTableCell>
-        <TeamTableCell width={15}>Actions</TeamTableCell>
-
-        {teams.map((team, i) => (
-          <Team
-            deleteTeam={deleteTeam}
-            key={team.id}
-            number={i + 1}
-            team={team}
-            updateTeam={updateTeam}
-            userOptions={userOptions}
-          />
-        ))}
-      </TeamTable>
-    )}
-    <TeamCreator
-      create={createTeam}
-      manuscriptsOptions={manuscriptsOptions}
-      typesOptions={typesOptions}
-    />
-  </Page>
-)
-
-export default TeamsManager
diff --git a/app/components/component-teams-manager/src/components/TeamsManager.test.js b/app/components/component-teams-manager/src/components/TeamsManager.test.js
deleted file mode 100644
index dd22df93a54aa3d0cc1aa6e457e75d55f13f681e..0000000000000000000000000000000000000000
--- a/app/components/component-teams-manager/src/components/TeamsManager.test.js
+++ /dev/null
@@ -1,94 +0,0 @@
-import React from 'react'
-import Enzyme, { shallow } from 'enzyme'
-import Adapter from 'enzyme-adapter-react-16'
-
-import TeamsManager from './TeamsManager'
-
-import Team from './Team'
-import TeamCreator from './TeamCreator'
-
-// this should be elsewhere
-Enzyme.configure({ adapter: new Adapter() })
-
-jest.mock('config', () => ({
-  'pubsweet-client': {},
-  authsome: {
-    mode: 'authsome',
-    teams: {
-      seniorEditor: {
-        name: 'Senior Editors',
-        permissions: '',
-      },
-      handlingEditor: {
-        name: 'Handling Editors',
-        permissions: '',
-      },
-      managingEditor: {
-        name: 'Managing Editors',
-        permissions: '',
-      },
-      reviewer: {
-        name: 'Reviewer',
-        permissions: '',
-      },
-    },
-  },
-}))
-
-describe('TeamsManager', () => {
-  const makeWrapper = (props = {}) => {
-    props = Object.assign(
-      {
-        teams: [],
-        userOptions: [
-          { id: '1', username: 'author' },
-          { id: '2', username: 'managing Editor' },
-        ],
-      },
-      props,
-    )
-
-    return shallow(<TeamsManager {...props} />)
-  }
-
-  it('shows nothing when there are no teams', () => {
-    const teammanager = makeWrapper()
-    expect(teammanager.find(Team)).toHaveLength(0)
-    expect(teammanager.find(TeamCreator)).toHaveLength(1)
-  })
-
-  it('shows a list of teams created', () => {
-    const teammanager = makeWrapper({
-      teams: [
-        {
-          id: 1,
-          name: 'team1',
-          teamType: {
-            name: 'Senior Editors',
-            permissions: '',
-          },
-          object: {
-            type: 'collection',
-            id: '1',
-          },
-          members: [],
-        },
-        {
-          id: 1,
-          name: 'team2',
-          teamType: {
-            name: 'Handling Editors',
-            permissions: '',
-          },
-          object: {
-            type: 'collection',
-            id: '1',
-          },
-          members: [],
-        },
-      ],
-    })
-
-    expect(teammanager.find(Team)).toHaveLength(2)
-  })
-})
diff --git a/app/components/component-teams-manager/src/components/TeamsManagerPage.js b/app/components/component-teams-manager/src/components/TeamsManagerPage.js
deleted file mode 100644
index 9a4c7cd09b5e6a982bbc196665cd1bc6813261ae..0000000000000000000000000000000000000000
--- a/app/components/component-teams-manager/src/components/TeamsManagerPage.js
+++ /dev/null
@@ -1,161 +0,0 @@
-import { compose } from 'recompose'
-import { omit } from 'lodash'
-import config from 'config'
-import { graphql } from '@apollo/client/react/hoc'
-import gql from 'graphql-tag'
-
-import queries from './graphql/queries'
-import TeamsManager from './TeamsManager'
-
-const deleteTeamMutation = gql`
-  mutation($id: ID) {
-    deleteTeam(id: $id) {
-      id
-    }
-  }
-`
-
-const createTeamMutation = gql`
-  mutation($input: TeamInput!) {
-    createTeam(input: $input) {
-      id
-      type
-      role
-      name
-      object {
-        objectId
-        objectType
-      }
-      members {
-        user {
-          id
-          username
-        }
-      }
-    }
-  }
-`
-
-const updateTeamMutation = gql`
-  mutation($id: ID, $input: TeamInput) {
-    updateTeam(id: $id, input: $input) {
-      id
-      type
-      role
-      name
-      object {
-        objectId
-        objectType
-      }
-      members {
-        user {
-          id
-          username
-        }
-      }
-    }
-  }
-`
-
-export default compose(
-  graphql(queries.teamManager, {
-    props: ({ data }) => {
-      const userOptions = ((data || {}).users || []).map(user => ({
-        value: user.id,
-        label: user.username,
-      }))
-
-      const manuscriptsOptions = ((data || {}).manuscripts || []).map(manu => ({
-        value: manu.id,
-        label: manu.meta.title,
-      }))
-
-      const types = config.authsome.teams
-      const typesOptions = Object.keys(types).map(type => ({
-        value: type,
-        label: `${types[type].name} ${types[type].permissions}`,
-      }))
-      return {
-        teams: (data || {}).teams,
-        manuscriptsOptions,
-        userOptions,
-        typesOptions,
-      }
-    },
-  }),
-  graphql(updateTeamMutation, {
-    props: ({ mutate }) => {
-      const updateTeam = (members, team) => {
-        let input = {
-          ...team,
-          objectId: team.object.objectId,
-          objectType: team.object.objectType,
-          object: undefined,
-        }
-
-        input.members = members.map(m => ({ user: { id: m } }))
-        input = omit(input, ['id', 'object.__typename', '__typename'])
-
-        mutate({
-          variables: {
-            id: team.id,
-            input,
-          },
-        })
-      }
-
-      return {
-        updateTeam,
-      }
-    },
-  }),
-  graphql(deleteTeamMutation, {
-    props: ({ mutate }) => {
-      const deleteTeam = data => {
-        mutate({
-          variables: {
-            id: data.id,
-          },
-        })
-      }
-
-      return {
-        deleteTeam,
-      }
-    },
-    options: {
-      update: (proxy, { data: { deleteTeam } }) => {
-        const data = proxy.readQuery({ query: queries.teamManager })
-        const teamsIndex = data.teams.findIndex(
-          team => team.id === deleteTeam.id,
-        )
-        if (teamsIndex > -1) {
-          data.teams.splice(teamsIndex, 1)
-          proxy.writeQuery({ query: queries.teamManager, data })
-        }
-      },
-    },
-  }),
-  graphql(createTeamMutation, {
-    props: ({ mutate }) => {
-      const createTeam = input => {
-        mutate({
-          variables: {
-            input,
-          },
-        })
-      }
-
-      return {
-        createTeam,
-      }
-    },
-    options: {
-      update: (proxy, { data: { createTeam } }) => {
-        const data = proxy.readQuery({ query: queries.teamManager })
-        data.teams.push(createTeam)
-        proxy.writeQuery({ query: queries.teamManager, data })
-      },
-    },
-  }),
-)(TeamsManager)
diff --git a/app/components/component-teams-manager/src/components/graphql/queries/index.js b/app/components/component-teams-manager/src/components/graphql/queries/index.js
deleted file mode 100644
index 5f31211a9a822ee407c42ee44f00561366bf183b..0000000000000000000000000000000000000000
--- a/app/components/component-teams-manager/src/components/graphql/queries/index.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import gql from 'graphql-tag'
-
-const fragmentFields = `
-  id
-  created
-  meta {
-    manuscriptId
-    title
-  }
-`
-
-export default {
-  teamManager: gql`
-  query {
-    teams {
-      id
-      role
-      name
-      object {
-        objectId
-        objectType
-      }
-      members {
-        user {
-          id
-        }
-      }
-    }
-
-    users {
-      id
-      username
-      admin
-    }
-
-    manuscripts {
-      ${fragmentFields}
-      manuscriptVersions {
-        ${fragmentFields}
-      }
-    }
-  }
-  `,
-}
diff --git a/app/components/component-teams-manager/src/components/molecules/Page.js b/app/components/component-teams-manager/src/components/molecules/Page.js
deleted file mode 100644
index 26240d8774892d07923e907f5a20580062dbca68..0000000000000000000000000000000000000000
--- a/app/components/component-teams-manager/src/components/molecules/Page.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import styled from 'styled-components'
-import { th, grid } from '@pubsweet/ui-toolkit'
-
-const Page = styled.div`
-  padding: ${grid(2)};
-`
-
-const Section = styled.div`
-  margin: ${th('gridUnit')} 0;
-
-  &:not(:last-of-type) {
-    margin-bottom: calc(${th('gridUnit')} * 2);
-  }
-`
-
-const Heading = styled.div`
-  color: ${th('colorPrimary')};
-  font-family: ${th('fontReading')};
-  font-size: ${th('fontSizeHeading3')};
-  margin: ${th('gridUnit')} 0;
-  text-transform: uppercase;
-`
-
-const UploadContainer = styled.div`
-  display: flex;
-  justify-content: center;
-`
-
-export { Page, Section, Heading, UploadContainer }
diff --git a/app/components/component-teams-manager/src/components/molecules/Table.js b/app/components/component-teams-manager/src/components/molecules/Table.js
deleted file mode 100644
index 4e7c813148d4f07c73b3b385e2fb94dfd85e638d..0000000000000000000000000000000000000000
--- a/app/components/component-teams-manager/src/components/molecules/Table.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import styled from 'styled-components'
-
-const TeamTable = styled.div`
-  display: flex;
-  flex-wrap: wrap;
-  margin: 0 0 3px 0;
-  padding: 0;
-`
-
-const TeamTableCell = styled.div`
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  box-sizing: border-box;
-  flex-grow: 1;
-  padding: 20px 10px 0 0;
-  list-style: none;
-  border: solid @bw white;
-  background: fade(slategrey, 20%);
-  width: ${props => props.width || 20}%;
-  > h4 {
-    margin: 0;
-  }
-`
-
-export { TeamTable, TeamTableCell }
diff --git a/app/components/component-teams-manager/src/index.js b/app/components/component-teams-manager/src/index.js
deleted file mode 100644
index 0b91ce74b9fa94404bcd77c1b1784b65875382e1..0000000000000000000000000000000000000000
--- a/app/components/component-teams-manager/src/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-module.exports = {
-  frontend: {
-    components: [() => require('./components/TeamsManagerPage')],
-  },
-}
diff --git a/app/components/shared/Dropzone.jsx b/app/components/shared/Dropzone.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..29f979895c8b23e1da7ae91c4551ecc3737c6a7c
--- /dev/null
+++ b/app/components/shared/Dropzone.jsx
@@ -0,0 +1,11 @@
+import React from 'react'
+import styled from 'styled-components'
+import ReactDropzone from 'react-dropzone'
+
+export const Dropzone = styled(({ disableUpload, ...props }) => (
+  <ReactDropzone {...props} />
+))`
+  border: none;
+  cursor: pointer;
+  ${({ disableUpload }) => disableUpload && 'pointer-events: none;'};
+`
diff --git a/app/components/shared/FilesUpload.js b/app/components/shared/FilesUpload.js
new file mode 100644
index 0000000000000000000000000000000000000000..6fc7c38dbf316c77ad71feed744bbb2536d49760
--- /dev/null
+++ b/app/components/shared/FilesUpload.js
@@ -0,0 +1,139 @@
+import React from 'react'
+import { cloneDeep, get } from 'lodash'
+import { FieldArray } from 'formik'
+import { grid, th } from '@pubsweet/ui-toolkit'
+import styled from 'styled-components'
+import { useMutation, gql } from '@apollo/client'
+import UploadingFile from './UploadingFile'
+import { Dropzone } from './Dropzone'
+import { Icon } from './Icon'
+
+const Root = styled.div`
+  border: 1px dashed ${th('colorBorder')};
+  height: ${grid(8)};
+  border-radius: ${th('borderRadius')};
+  text-align: center;
+  line-height: ${grid(8)};
+`
+
+const Files = styled.div`
+  margin-top: ${grid(2)};
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+  grid-gap: ${grid(2)};
+`
+
+const Message = styled.div`
+  display: flex;
+  width: 100%;
+  align-items: center;
+  justify-content: center;
+  svg {
+    margin-left: ${grid(1)};
+  }
+`
+
+const createFileMutation = gql`
+  mutation($file: Upload!, $meta: FileMetaInput) {
+    createFile(file: $file, meta: $meta) {
+      id
+      created
+      label
+      filename
+      fileType
+      mimeType
+      size
+      url
+    }
+  }
+`
+
+const DropzoneAndList = ({
+  form: { values, setFieldValue },
+  push,
+  insert,
+  createFile,
+  deleteFile,
+  fileType,
+  fieldName,
+}) => (
+  <>
+    <Dropzone
+      onDrop={async files => {
+        Array.from(files).forEach(async file => {
+          const data = await createFile(file)
+          push(data.createFile)
+        })
+      }}
+    >
+      {({ getRootProps, getInputProps }) => (
+        <Root {...getRootProps()} data-testid="dropzone">
+          <input {...getInputProps()} />
+          <Message>
+            Drag and drop your files here
+            <Icon color={th('colorPrimary')} inline>
+              file-plus
+            </Icon>
+          </Message>
+        </Root>
+      )}
+    </Dropzone>
+    <Files>
+      {cloneDeep(get(values, fieldName) || [])
+        .filter(val => (fileType ? val.fileType === fileType : true))
+        .map(val => {
+          val.name = val.filename
+          return <UploadingFile file={val} key={val.name} uploaded />
+        })}
+    </Files>
+  </>
+)
+
+const FilesUpload = ({
+  fileType,
+  fieldName = 'files',
+  containerId,
+  containerName,
+  initializeContainer,
+}) => {
+  const [createFile] = useMutation(createFileMutation)
+  // const [deleteFile] = useMutation(deleteFileMutation)
+
+  const createFileWithMeta = async file => {
+    const meta = {
+      filename: file.name,
+      mimeType: file.type,
+      size: file.size,
+      fileType,
+    }
+
+    // Create a container/parent for these files if one doesn't exist
+    const localContainerId = containerId || (await initializeContainer())
+
+    meta[`${containerName}Id`] = localContainerId
+
+    const { data } = await createFile({
+      variables: {
+        file,
+        meta,
+      },
+    })
+    return data
+  }
+
+  return (
+    <FieldArray
+      name={fieldName}
+      render={formikProps => (
+        <DropzoneAndList
+          createFile={createFileWithMeta}
+          // deleteFile={deleteFile}
+          fieldName={fieldName}
+          fileType={fileType}
+          {...formikProps}
+        />
+      )}
+    />
+  )
+}
+export { FilesUpload }
diff --git a/app/components/shared/Icon.jsx b/app/components/shared/Icon.jsx
index 9ffcb79c7f23c2a0b48e2bba5d5956ed09cd935e..311230387ec34741d874b7c81f37dec7963776be 100644
--- a/app/components/shared/Icon.jsx
+++ b/app/components/shared/Icon.jsx
@@ -6,15 +6,13 @@ import styled from 'styled-components'
 import { th } from '@pubsweet/ui-toolkit'
 
 const IconWrapper = styled.div`
-  display: flex;
-  -webkit-box-align: center;
+  display: ${({ inline }) => (inline ? 'inline-flex' : 'flex')};
   align-items: center;
-  -webkit-box-pack: center;
   justify-content: center;
   opacity: 1;
   position: relative;
   border-radius: 6px;
-  padding: ${props => (props.noPadding ? '0' : '8px 12px')};
+  padding: ${props => (props.noPadding || props.inline ? '0' : '8px 12px')};
 
   svg {
     stroke: ${props => props.color || props.theme.colorText};
@@ -29,6 +27,7 @@ export const Icon = ({
   color,
   size = 3,
   noPadding,
+  inline,
   ...props
 }) => {
   const name = _.upperFirst(_.camelCase(children))
@@ -37,6 +36,7 @@ export const Icon = ({
     <IconWrapper
       className={className}
       color={color}
+      inline={inline}
       noPadding={noPadding}
       role="img"
       size={size}
diff --git a/app/components/shared/UploadingFile.js b/app/components/shared/UploadingFile.js
new file mode 100644
index 0000000000000000000000000000000000000000..5c7e9ab16caf1ec5577f84b795e08e79d493c559
--- /dev/null
+++ b/app/components/shared/UploadingFile.js
@@ -0,0 +1,121 @@
+import React from 'react'
+import styled from 'styled-components'
+import { th, grid } from '@pubsweet/ui-toolkit'
+
+const Icon = styled.div`
+  background: ${th('colorFurniture')};
+  height: ${grid(10)};
+  margin-bottom: ${th('gridUnit')};
+  opacity: 0.5;
+  position: relative;
+  width: ${grid(10)};
+  overflow: hidden;
+  padding: ${grid(1)};
+  img {
+    width: ${grid(8)};
+  }
+`
+
+// const Progress = styled.div`
+//   color: ${th('colorTextReverse')};
+//   display: block;
+//   position: absolute;
+//   bottom: ${th('gridUnit')};
+//   left: ${grid(4)};
+// `
+
+const Extension = styled.div`
+  background: ${th('colorText')};
+  color: ${th('colorTextReverse')};
+  font-size: ${th('fontSizeBaseSmall')};
+  line-height: ${th('lineHeightBaseSmall')};
+  left: ${grid(2)};
+  position: absolute;
+  right: 0;
+  text-align: center;
+  text-transform: uppercase;
+  top: ${grid(2)};
+`
+
+const Filename = styled.div`
+  color: ${th('colorText')};
+  font-size: ${th('fontSizeBaseSmall')};
+  line-height: ${th('lineHeightBaseSmall')};
+  font-style: italic;
+  max-width: ${grid(30)};
+  text-overflow: ellipsis;
+  overflow: hidden;
+`
+
+const Uploading = styled.div`
+  align-items: center;
+  display: block;
+  flex-direction: column;
+  margin-bottom: ${grid(3)};
+  margin-right: ${grid(3)};
+  position: relative;
+`
+
+const Uploaded = styled(Uploading)`
+  &:hover ${Extension} {
+    background: ${th('colorTextReverse')};
+    color: ${th('colorPrimary')};
+  }
+
+  &:hover ${Icon} {
+    opacity: 1;
+    background: ${th('colorPrimary')};
+    // transform: skewY(6deg) rotate(-6deg);
+  }
+
+  &:hover::after,
+  &:hover::before {
+    transform: scaleX(1);
+  }
+`
+
+const ErrorWrapper = styled.div`
+  background: ${th('colorError')};
+  border: calc(${th('borderWidth')} * 2) ${th('borderStyle')}
+    ${th('colorTextReverse')};
+  color: ${th('colorTextReverse')};
+  font-size: ${th('fontSizeBaseSmall')};
+  line-height: ${th('lineHeightBaseSmall')};
+  letter-spacing: 0.01em;
+  opacity: 1;
+  padding: ${th('gridUnit')} ${th('gridUnit')};
+  position: absolute;
+  top: 25%;
+  z-index: 4;
+`
+
+const getFileExtension = ({ name }) => name.replace(/^.+\./, '')
+
+const UploadingFile = ({ file, progress, error, uploaded }) => {
+  const Root = uploaded ? Uploaded : Uploading
+
+  const extension = getFileExtension(file)
+  const isImage = file?.mimeType.startsWith('image/')
+
+  return (
+    <Root>
+      {!!error && <ErrorWrapper>{error}</ErrorWrapper>}
+      <Icon>
+        {isImage && <img alt={file.name} src={file.url} />}
+        {/* {!!progress && <Progress>{progress * 100}%</Progress>} */}
+        <Extension>{extension}</Extension>
+      </Icon>
+      <Filename>
+        {uploaded ? (
+          <a download={file.name} href={file.url} title={file.name}>
+            {file.name}
+          </a>
+        ) : (
+          file.name
+        )}
+      </Filename>
+    </Root>
+  )
+}
+
+export default UploadingFile
diff --git a/app/components/shared/index.js b/app/components/shared/index.js
index 9c03d0839662980df9d89035eb141a227f8a089b..beaf45b435875c15e1dd26d777a72b14deb3cd1c 100644
--- a/app/components/shared/index.js
+++ b/app/components/shared/index.js
@@ -8,3 +8,5 @@ export * from './Table'
 export * from './General'
 export * from './Badge'
 export * from './Select'
+export * from './Dropzone'
+export * from './FilesUpload'
diff --git a/cypress/dumps/decision_completed.sql b/cypress/dumps/decision_completed.sql
new file mode 100644
index 0000000000000000000000000000000000000000..131e3333d986432395c5488837c6c94cbd0f1a5b
--- /dev/null
+++ b/cypress/dumps/decision_completed.sql
@@ -0,0 +1,886 @@
+--
+-- PostgreSQL database dump
+--
+
+-- Dumped from database version 10.5
+-- Dumped by pg_dump version 10.5
+
+SET statement_timeout = 0;
+SET lock_timeout = 0;
+SET idle_in_transaction_session_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = on;
+SELECT pg_catalog.set_config('search_path', '', false);
+SET check_function_bodies = false;
+SET client_min_messages = warning;
+SET row_security = off;
+
+--
+-- Name: pgboss; Type: SCHEMA; Schema: -; Owner: test
+--
+
+CREATE SCHEMA pgboss;
+
+
+ALTER SCHEMA pgboss OWNER TO test;
+
+--
+-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: 
+--
+
+CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
+
+
+--
+-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: 
+--
+
+COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
+
+
+--
+-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: 
+--
+
+CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public;
+
+
+--
+-- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: 
+--
+
+COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions';
+
+
+--
+-- Name: job_state; Type: TYPE; Schema: pgboss; Owner: test
+--
+
+CREATE TYPE pgboss.job_state AS ENUM (
+    'created',
+    'retry',
+    'active',
+    'completed',
+    'expired',
+    'cancelled',
+    'failed'
+);
+
+
+ALTER TYPE pgboss.job_state OWNER TO test;
+
+SET default_tablespace = '';
+
+SET default_with_oids = false;
+
+--
+-- Name: archive; Type: TABLE; Schema: pgboss; Owner: test
+--
+
+CREATE TABLE pgboss.archive (
+    id uuid NOT NULL,
+    name text NOT NULL,
+    priority integer NOT NULL,
+    data jsonb,
+    state pgboss.job_state NOT NULL,
+    retrylimit integer NOT NULL,
+    retrycount integer NOT NULL,
+    retrydelay integer NOT NULL,
+    retrybackoff boolean NOT NULL,
+    startafter timestamp with time zone NOT NULL,
+    startedon timestamp with time zone,
+    singletonkey text,
+    singletonon timestamp without time zone,
+    expirein interval NOT NULL,
+    createdon timestamp with time zone NOT NULL,
+    completedon timestamp with time zone,
+    archivedon timestamp with time zone DEFAULT now() NOT NULL
+);
+
+
+ALTER TABLE pgboss.archive OWNER TO test;
+
+--
+-- Name: job; Type: TABLE; Schema: pgboss; Owner: test
+--
+
+CREATE TABLE pgboss.job (
+    id uuid DEFAULT public.gen_random_uuid() NOT NULL,
+    name text NOT NULL,
+    priority integer DEFAULT 0 NOT NULL,
+    data jsonb,
+    state pgboss.job_state DEFAULT 'created'::pgboss.job_state NOT NULL,
+    retrylimit integer DEFAULT 0 NOT NULL,
+    retrycount integer DEFAULT 0 NOT NULL,
+    retrydelay integer DEFAULT 0 NOT NULL,
+    retrybackoff boolean DEFAULT false NOT NULL,
+    startafter timestamp with time zone DEFAULT now() NOT NULL,
+    startedon timestamp with time zone,
+    singletonkey text,
+    singletonon timestamp without time zone,
+    expirein interval DEFAULT '00:15:00'::interval NOT NULL,
+    createdon timestamp with time zone DEFAULT now() NOT NULL,
+    completedon timestamp with time zone
+);
+
+
+ALTER TABLE pgboss.job OWNER TO test;
+
+--
+-- Name: version; Type: TABLE; Schema: pgboss; Owner: test
+--
+
+CREATE TABLE pgboss.version (
+    version text NOT NULL
+);
+
+
+ALTER TABLE pgboss.version OWNER TO test;
+
+--
+-- Name: aliases; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.aliases (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    name text,
+    email text,
+    aff text
+);
+
+
+ALTER TABLE public.aliases OWNER TO test;
+
+--
+-- Name: channel_members; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.channel_members (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    user_id uuid NOT NULL,
+    channel_id uuid NOT NULL
+);
+
+
+ALTER TABLE public.channel_members OWNER TO test;
+
+--
+-- Name: channels; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.channels (
+    id uuid NOT NULL,
+    manuscript_id uuid,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    topic text,
+    type text
+);
+
+
+ALTER TABLE public.channels OWNER TO test;
+
+--
+-- Name: entities; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.entities (
+    id uuid NOT NULL,
+    data jsonb
+);
+
+
+ALTER TABLE public.entities OWNER TO test;
+
+--
+-- Name: files; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.files (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    label text,
+    file_type text,
+    filename text,
+    url text,
+    mime_type text,
+    size integer,
+    type text NOT NULL,
+    manuscript_id uuid,
+    review_comment_id uuid,
+    CONSTRAINT exactly_one_file_owner CHECK (((((manuscript_id IS NOT NULL))::integer + ((review_comment_id IS NOT NULL))::integer) = 1))
+);
+
+
+ALTER TABLE public.files OWNER TO test;
+
+--
+-- Name: identities; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.identities (
+    id uuid NOT NULL,
+    user_id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    type text NOT NULL,
+    identifier text,
+    name text,
+    aff text,
+    oauth jsonb,
+    is_default boolean
+);
+
+
+ALTER TABLE public.identities OWNER TO test;
+
+--
+-- Name: manuscripts; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.manuscripts (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    parent_id uuid,
+    submitter_id uuid,
+    status text,
+    decision text,
+    authors jsonb,
+    suggestions jsonb,
+    meta jsonb,
+    submission jsonb,
+    type text NOT NULL
+);
+
+
+ALTER TABLE public.manuscripts OWNER TO test;
+
+--
+-- Name: messages; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.messages (
+    id uuid NOT NULL,
+    user_id uuid NOT NULL,
+    channel_id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    content text
+);
+
+
+ALTER TABLE public.messages OWNER TO test;
+
+--
+-- Name: migrations; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.migrations (
+    id text NOT NULL,
+    run_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP
+);
+
+
+ALTER TABLE public.migrations OWNER TO test;
+
+--
+-- Name: review_comments; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.review_comments (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    review_id uuid,
+    user_id uuid,
+    content text,
+    comment_type text,
+    type text
+);
+
+
+ALTER TABLE public.review_comments OWNER TO test;
+
+--
+-- Name: reviews; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.reviews (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    recommendation text,
+    is_decision boolean DEFAULT false,
+    user_id uuid,
+    manuscript_id uuid,
+    type text NOT NULL
+);
+
+
+ALTER TABLE public.reviews OWNER TO test;
+
+--
+-- Name: team_members; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.team_members (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    status character varying(255),
+    team_id uuid,
+    user_id uuid,
+    alias_id uuid
+);
+
+
+ALTER TABLE public.team_members OWNER TO test;
+
+--
+-- Name: teams; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.teams (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    name text,
+    role text NOT NULL,
+    members jsonb,
+    owners jsonb,
+    global boolean,
+    type text NOT NULL,
+    manuscript_id uuid
+);
+
+
+ALTER TABLE public.teams OWNER TO test;
+
+--
+-- Name: users; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.users (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    admin boolean,
+    email text,
+    username text,
+    password_hash text,
+    teams jsonb,
+    password_reset_token text,
+    password_reset_timestamp timestamp with time zone,
+    type text NOT NULL,
+    profile_picture text,
+    online boolean
+);
+
+
+ALTER TABLE public.users OWNER TO test;
+
+--
+-- Data for Name: archive; Type: TABLE DATA; Schema: pgboss; Owner: test
+--
+
+
+
+--
+-- Data for Name: job; Type: TABLE DATA; Schema: pgboss; Owner: test
+--
+
+
+
+--
+-- Data for Name: version; Type: TABLE DATA; Schema: pgboss; Owner: test
+--
+
+INSERT INTO pgboss.version (version) VALUES ('11');
+
+
+--
+-- Data for Name: aliases; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+
+
+--
+-- Data for Name: channel_members; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+
+
+--
+-- Data for Name: channels; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('afd95c06-011b-46e3-9985-964283d51c4a', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('75540565-6619-495f-a8c5-d6bf11ece72d', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Editorial discussion', 'editorial');
+
+
+--
+-- Data for Name: entities; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+
+
+--
+-- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+
+
+--
+-- Data for Name: identities; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('d341a633-cdce-4a7f-a9ad-5afc03cd0dd1', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.741+02', '2020-07-21 16:17:25.87+02', 'orcid', '0000-0002-0564-2016', 'Emily Clay', NULL, '{"accessToken": "079a1165-31e5-4b59-9a99-d80ff7a21ebf", "refreshToken": "ccadc737-defc-419e-823b-a9f3673848ba"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('bcda196e-765a-42c8-94da-ca2e43b80f96', '3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.721+02', '2020-07-21 16:33:26.742+02', 'orcid', '0000-0002-5641-5729', 'Sinead Sullivan', NULL, '{"accessToken": "ef1ed3ec-8371-41b2-a136-fd196ae52a72", "refreshToken": "6972dace-d9a6-4cd3-a2ad-ec7eb3e457c7"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('4af83984-6359-47c5-a075-5ddfa9c555d9', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.127+02', '2020-07-21 16:35:07.104+02', 'orcid', '0000-0002-7645-9921', 'Sherry Crofoot', NULL, '{"accessToken": "2ad4e130-0775-4e13-87fb-8e8f5a0570ae", "refreshToken": "159933d9-2020-4c02-bdfb-163af41017dc"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('acfa1777-0aec-4fe1-bc16-92bb9d19e884', '85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.384+02', '2020-07-21 16:35:39.358+02', 'orcid', '0000-0002-9429-4446', 'Elaine Barnes', NULL, '{"accessToken": "dcf07bc7-e59c-41b3-9ce0-924ac20aeeea", "refreshToken": "ae49d6a1-8e62-419d-8767-4a3ec22c1950"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('88c85115-d83c-42d7-a1a1-0139827977da', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.975+02', '2020-07-21 16:36:26.059+02', 'orcid', '0000-0001-5956-7341', 'Gale Davis', NULL, '{"accessToken": "3e9f6f6c-7cc0-4afa-9fdf-6ed377c36aad", "refreshToken": "80b1e911-df97-43f1-9f11-17b61913f6d7"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('049f91da-c84e-4b80-be2e-6e0cfca7a136', '231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.611+02', '2020-07-22 14:18:37.745+02', 'orcid', '0000-0003-2536-230X', 'Test Account', NULL, '{"accessToken": "eb551178-79e5-4189-8c5f-0a553092a9b5", "refreshToken": "4506fa5f-bd77-4867-afb4-0b07ea5302d6"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('2fb8359c-239c-43fa-91f5-1ff2058272a6', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.604+02', '2020-07-24 15:21:55.7+02', 'orcid', '0000-0003-1838-2441', 'Joanne Pilger', NULL, '{"accessToken": "842de329-ef16-4461-b83b-e8fe57238904", "refreshToken": "524fbdc5-9c67-4b4c-af17-2ce4cf294e88"}', true);
+
+
+--
+-- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.9+02', '2020-08-13 15:12:29.223+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'accepted', 'accepted', NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keyword", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
+
+
+--
+-- Data for Name: messages; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+
+
+--
+-- Data for Name: migrations; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-08-12 14:59:10.439327+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-08-12 14:59:10.452184+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-08-12 14:59:10.463129+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1580908536-add-identities.sql', '2020-08-12 14:59:10.477935+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581371297-migrate-users-to-identities.js', '2020-08-12 14:59:10.499722+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581450834-manuscript.sql', '2020-08-12 14:59:10.509542+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1582930582-drop-fragments-and-collections.js', '2020-08-12 14:59:10.519573+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585323910-add-channels.sql', '2020-08-12 14:59:10.533012+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585344885-add-messages.sql', '2020-08-12 14:59:10.544214+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585513226-add-profile-pic.sql', '2020-08-12 14:59:10.549847+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities-constraint.sql', '2020-08-12 14:59:10.558714+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830547-review.sql', '2020-08-12 14:59:10.571826+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-add-review-comments.sql', '2020-08-12 14:59:10.583466+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-initial-team-migration.sql', '2020-08-12 14:59:10.611618+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020-08-12 14:59:10.627188+02');
+
+
+--
+-- Data for Name: review_comments; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('f73fdbd8-6fb6-4d7a-a74a-ab2c2c07b572', '2020-08-13 15:09:33.838+02', '2020-08-13 15:09:33.838+02', '5a06fcf3-d368-4f65-917c-1acdb73fcc71', NULL, '<p>Great paper, congratulations! Gale Davis</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('5c86da62-bc7f-4d23-8968-62bd5a56a52b', '2020-08-13 15:09:34.88+02', '2020-08-13 15:09:34.88+02', '5a06fcf3-d368-4f65-917c-1acdb73fcc71', NULL, '<p>This is a very important paper. Gale Davis</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('ef96b249-dab6-4f7e-b24e-571b340b9b41', '2020-08-13 15:09:38.761+02', '2020-08-13 15:09:38.761+02', '7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', NULL, '<p>Great paper, congratulations! Sherry Crofoot</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('04a8a70e-7787-4fd6-bdca-bfac3aa86951', '2020-08-13 15:09:40.155+02', '2020-08-13 15:09:40.155+02', '7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', NULL, '<p>This is a very important paper. Sherry Crofoot</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('7ccda096-07c2-4569-ad0f-5b3574db205e', '2020-08-13 15:09:44.046+02', '2020-08-13 15:09:44.046+02', '3a34e0ef-6695-4268-b901-60de20f9cf4e', NULL, '<p>Great paper, congratulations! Elaine Barnes</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('c8b7d1c9-aef2-47d0-a9ad-c9d46706e79e', '2020-08-13 15:09:45.548+02', '2020-08-13 15:09:45.548+02', '3a34e0ef-6695-4268-b901-60de20f9cf4e', NULL, '<p>This is a very important paper. Elaine Barnes</p>', 'confidential', 'ReviewComment');
+
+
+--
+-- Data for Name: reviews; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('5a06fcf3-d368-4f65-917c-1acdb73fcc71', '2020-08-13 15:09:32.358+02', '2020-08-13 15:09:34.925+02', 'accepted', false, '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', '2020-08-13 15:09:36.501+02', '2020-08-13 15:09:40.159+02', 'accepted', false, '0da0bbec-9261-4706-b990-0c10aa3cc6b4', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('3a34e0ef-6695-4268-b901-60de20f9cf4e', '2020-08-13 15:09:41.911+02', '2020-08-13 15:09:45.561+02', 'accepted', false, '85e1300e-003c-4e96-987b-23812f902477', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('7d962c82-322e-4b02-8981-5f26479433ab', '2020-08-13 15:12:28.737+02', '2020-08-13 15:12:28.737+02', 'accepted', true, '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
+
+
+--
+-- Data for Name: team_members; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('6505126d-2ecd-498b-a27d-18eafbdbc8a6', '2020-08-12 16:16:44.923+02', '2020-08-12 16:16:44.923+02', NULL, 'c31e3116-6176-45b5-b52c-6f7cbfc86007', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('031ae180-e94e-458d-86b7-f2dc1d456e2e', '2020-08-12 16:17:14.795+02', '2020-08-12 16:17:14.795+02', NULL, 'bf800912-56c4-44eb-b425-64e51a9824fe', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('c4ac4fc0-3fae-43b0-89ee-bf7f8fd9885f', '2020-08-12 16:17:43.262+02', '2020-08-13 15:09:41.898+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('69e896c6-0fdb-421d-a638-1ef7670c03c9', '2020-08-12 16:17:43.884+02', '2020-08-13 15:09:41.898+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('12c33ef5-4fc3-4d48-8309-a90c0461113c', '2020-08-12 16:17:44.547+02', '2020-08-13 15:09:45.782+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '85e1300e-003c-4e96-987b-23812f902477', NULL);
+
+
+--
+-- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('c31e3116-6176-45b5-b52c-6f7cbfc86007', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Author', 'author', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('bf800912-56c4-44eb-b425-64e51a9824fe', '2020-08-12 16:17:14.789+02', '2020-08-12 16:17:14.789+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('0719d132-bb6c-49d9-9122-7911a48cfd60', '2020-08-12 16:17:43.258+02', '2020-08-13 15:09:41.898+02', 'Reviewers', 'reviewer', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+
+
+--
+-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-12 16:17:13.598+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-12 16:17:14.868+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-08-13 15:09:35.913+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-08-13 15:09:41.198+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.381+02', '2020-08-13 15:09:46.741+02', NULL, NULL, '0000000294294446', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser1.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-13 15:12:29.67+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
+
+
+--
+-- Name: job job_pkey; Type: CONSTRAINT; Schema: pgboss; Owner: test
+--
+
+ALTER TABLE ONLY pgboss.job
+    ADD CONSTRAINT job_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: version version_pkey; Type: CONSTRAINT; Schema: pgboss; Owner: test
+--
+
+ALTER TABLE ONLY pgboss.version
+    ADD CONSTRAINT version_pkey PRIMARY KEY (version);
+
+
+--
+-- Name: aliases aliases_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.aliases
+    ADD CONSTRAINT aliases_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: channel_members channel_members_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.channel_members
+    ADD CONSTRAINT channel_members_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: channels channels_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.channels
+    ADD CONSTRAINT channels_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: entities entities_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.entities
+    ADD CONSTRAINT entities_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: files files_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: identities identities_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.identities
+    ADD CONSTRAINT identities_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: manuscripts manuscripts_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.manuscripts
+    ADD CONSTRAINT manuscripts_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: messages messages_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.messages
+    ADD CONSTRAINT messages_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.migrations
+    ADD CONSTRAINT migrations_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: review_comments review_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: reviews reviews_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.reviews
+    ADD CONSTRAINT reviews_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: team_members team_members_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.team_members
+    ADD CONSTRAINT team_members_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: teams teams_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.teams
+    ADD CONSTRAINT teams_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: users users_email_key; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.users
+    ADD CONSTRAINT users_email_key UNIQUE (email);
+
+
+--
+-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.users
+    ADD CONSTRAINT users_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: users users_username_key; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.users
+    ADD CONSTRAINT users_username_key UNIQUE (username);
+
+
+--
+-- Name: archive_archivedon_idx; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE INDEX archive_archivedon_idx ON pgboss.archive USING btree (archivedon);
+
+
+--
+-- Name: archive_id_idx; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE INDEX archive_id_idx ON pgboss.archive USING btree (id);
+
+
+--
+-- Name: job_name; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE INDEX job_name ON pgboss.job USING btree (name text_pattern_ops);
+
+
+--
+-- Name: job_singletonkey; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE UNIQUE INDEX job_singletonkey ON pgboss.job USING btree (name, singletonkey) WHERE ((state < 'completed'::pgboss.job_state) AND (singletonon IS NULL));
+
+
+--
+-- Name: job_singletonkeyon; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE UNIQUE INDEX job_singletonkeyon ON pgboss.job USING btree (name, singletonon, singletonkey) WHERE (state < 'expired'::pgboss.job_state);
+
+
+--
+-- Name: job_singletonon; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE UNIQUE INDEX job_singletonon ON pgboss.job USING btree (name, singletonon) WHERE ((state < 'expired'::pgboss.job_state) AND (singletonkey IS NULL));
+
+
+--
+-- Name: channel_members_idx; Type: INDEX; Schema: public; Owner: test
+--
+
+CREATE INDEX channel_members_idx ON public.channel_members USING btree (user_id, channel_id);
+
+
+--
+-- Name: is_default_idx; Type: INDEX; Schema: public; Owner: test
+--
+
+CREATE UNIQUE INDEX is_default_idx ON public.identities USING btree (is_default, user_id) WHERE (is_default IS TRUE);
+
+
+--
+-- Name: team_members_team_id_user_id_idx; Type: INDEX; Schema: public; Owner: test
+--
+
+CREATE INDEX team_members_team_id_user_id_idx ON public.team_members USING btree (team_id, user_id);
+
+
+--
+-- Name: teams_manuscript_id_idx; Type: INDEX; Schema: public; Owner: test
+--
+
+CREATE INDEX teams_manuscript_id_idx ON public.teams USING btree (manuscript_id);
+
+
+--
+-- Name: channel_members channel_members_channel_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.channel_members
+    ADD CONSTRAINT channel_members_channel_id_fkey FOREIGN KEY (channel_id) REFERENCES public.channels(id);
+
+
+--
+-- Name: channel_members channel_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.channel_members
+    ADD CONSTRAINT channel_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: channels channels_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.channels
+    ADD CONSTRAINT channels_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- Name: files files_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- Name: files files_review_comment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_review_comment_id_fkey FOREIGN KEY (review_comment_id) REFERENCES public.review_comments(id) ON DELETE CASCADE;
+
+
+--
+-- Name: manuscripts manuscripts_submitter_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.manuscripts
+    ADD CONSTRAINT manuscripts_submitter_id_fkey FOREIGN KEY (submitter_id) REFERENCES public.users(id);
+
+
+--
+-- Name: messages messages_channel_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.messages
+    ADD CONSTRAINT messages_channel_id_fkey FOREIGN KEY (channel_id) REFERENCES public.channels(id) ON DELETE CASCADE;
+
+
+--
+-- Name: messages messages_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.messages
+    ADD CONSTRAINT messages_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: review_comments review_comments_review_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_review_id_fkey FOREIGN KEY (review_id) REFERENCES public.reviews(id) ON DELETE CASCADE;
+
+
+--
+-- Name: review_comments review_comments_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL;
+
+
+--
+-- Name: reviews reviews_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.reviews
+    ADD CONSTRAINT reviews_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- Name: identities sidentities_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.identities
+    ADD CONSTRAINT sidentities_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: team_members team_members_alias_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.team_members
+    ADD CONSTRAINT team_members_alias_id_fkey FOREIGN KEY (alias_id) REFERENCES public.aliases(id);
+
+
+--
+-- Name: team_members team_members_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.team_members
+    ADD CONSTRAINT team_members_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- Name: team_members team_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.team_members
+    ADD CONSTRAINT team_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: teams teams_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.teams
+    ADD CONSTRAINT teams_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- PostgreSQL database dump complete
+--
+
diff --git a/cypress/dumps/initialState.sql b/cypress/dumps/initialState.sql
index 943209725dc5fb3990a02ead7bcb4c509fa0b446..0efe5f96fd618984916c97112e7c973c47411111 100644
--- a/cypress/dumps/initialState.sql
+++ b/cypress/dumps/initialState.sql
@@ -25,28 +25,28 @@ CREATE SCHEMA pgboss;
 ALTER SCHEMA pgboss OWNER TO test;
 
 --
--- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: 
+-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner:
 --
 
 CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
 
 
 --
--- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: 
+-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
 --
 
 COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
 
 
 --
--- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: 
+-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner:
 --
 
 CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public;
 
 
 --
--- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: 
+-- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner:
 --
 
 COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions';
@@ -145,9 +145,9 @@ CREATE TABLE public.aliases (
     id uuid NOT NULL,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
     updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
-    name character varying(255),
-    email character varying(255),
-    aff character varying(255)
+    name text,
+    email text,
+    aff text
 );
 
 
@@ -175,7 +175,6 @@ ALTER TABLE public.channel_members OWNER TO test;
 CREATE TABLE public.channels (
     id uuid NOT NULL,
     manuscript_id uuid,
-    team_id uuid,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
     updated timestamp with time zone,
     topic text,
@@ -205,15 +204,16 @@ CREATE TABLE public.files (
     id uuid NOT NULL,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
     updated timestamp with time zone,
-    object text,
-    object_id uuid,
     label text,
     file_type text,
     filename text,
     url text,
     mime_type text,
     size integer,
-    type text NOT NULL
+    type text NOT NULL,
+    manuscript_id uuid,
+    review_comment_id uuid,
+    CONSTRAINT exactly_one_file_owner CHECK (((((manuscript_id IS NOT NULL))::integer + ((review_comment_id IS NOT NULL))::integer) = 1))
 );
 
 
@@ -289,6 +289,24 @@ CREATE TABLE public.migrations (
 
 ALTER TABLE public.migrations OWNER TO test;
 
+--
+-- Name: review_comments; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.review_comments (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    review_id uuid,
+    user_id uuid,
+    content text,
+    comment_type text,
+    type text
+);
+
+
+ALTER TABLE public.review_comments OWNER TO test;
+
 --
 -- Name: reviews; Type: TABLE; Schema: public; Owner: test
 --
@@ -299,7 +317,6 @@ CREATE TABLE public.reviews (
     updated timestamp with time zone,
     recommendation text,
     is_decision boolean DEFAULT false,
-    comments jsonb,
     user_id uuid,
     manuscript_id uuid,
     type text NOT NULL
@@ -335,11 +352,11 @@ CREATE TABLE public.teams (
     updated timestamp with time zone,
     name text,
     role text NOT NULL,
+    members jsonb,
     owners jsonb,
     global boolean,
     type text NOT NULL,
-    object_id uuid,
-    object_type character varying(255)
+    manuscript_id uuid
 );
 
 
@@ -421,13 +438,6 @@ INSERT INTO pgboss.version (version) VALUES ('11');
 -- Data for Name: identities; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('d341a633-cdce-4a7f-a9ad-5afc03cd0dd1', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.741+02', '2020-07-21 16:17:25.87+02', 'orcid', '0000-0002-0564-2016', 'Emily Clay', NULL, '{"accessToken": "079a1165-31e5-4b59-9a99-d80ff7a21ebf", "refreshToken": "ccadc737-defc-419e-823b-a9f3673848ba"}', true);
-INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('bcda196e-765a-42c8-94da-ca2e43b80f96', '3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.721+02', '2020-07-21 16:33:26.742+02', 'orcid', '0000-0002-5641-5729', 'Sinead Sullivan', NULL, '{"accessToken": "ef1ed3ec-8371-41b2-a136-fd196ae52a72", "refreshToken": "6972dace-d9a6-4cd3-a2ad-ec7eb3e457c7"}', true);
-INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('4af83984-6359-47c5-a075-5ddfa9c555d9', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.127+02', '2020-07-21 16:35:07.104+02', 'orcid', '0000-0002-7645-9921', 'Sherry Crofoot', NULL, '{"accessToken": "2ad4e130-0775-4e13-87fb-8e8f5a0570ae", "refreshToken": "159933d9-2020-4c02-bdfb-163af41017dc"}', true);
-INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('acfa1777-0aec-4fe1-bc16-92bb9d19e884', '85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.384+02', '2020-07-21 16:35:39.358+02', 'orcid', '0000-0002-9429-4446', 'Elaine Barnes', NULL, '{"accessToken": "dcf07bc7-e59c-41b3-9ce0-924ac20aeeea", "refreshToken": "ae49d6a1-8e62-419d-8767-4a3ec22c1950"}', true);
-INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('88c85115-d83c-42d7-a1a1-0139827977da', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.975+02', '2020-07-21 16:36:26.059+02', 'orcid', '0000-0001-5956-7341', 'Gale Davis', NULL, '{"accessToken": "3e9f6f6c-7cc0-4afa-9fdf-6ed377c36aad", "refreshToken": "80b1e911-df97-43f1-9f11-17b61913f6d7"}', true);
-INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('049f91da-c84e-4b80-be2e-6e0cfca7a136', '231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.611+02', '2020-07-22 14:18:37.745+02', 'orcid', '0000-0003-2536-230X', 'Test Account', NULL, '{"accessToken": "eb551178-79e5-4189-8c5f-0a553092a9b5", "refreshToken": "4506fa5f-bd77-4867-afb4-0b07ea5302d6"}', true);
-INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('2fb8359c-239c-43fa-91f5-1ff2058272a6', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.604+02', '2020-07-24 15:21:55.7+02', 'orcid', '0000-0003-1838-2441', 'Joanne Pilger', NULL, '{"accessToken": "842de329-ef16-4461-b83b-e8fe57238904", "refreshToken": "524fbdc5-9c67-4b4c-af17-2ce4cf294e88"}', true);
 
 
 --
@@ -446,24 +456,27 @@ INSERT INTO public.identities (id, user_id, created, updated, type, identifier,
 -- Data for Name: migrations; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-07-21 16:01:00.856209+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-files.sql', '2020-07-21 16:01:00.866487+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-review.sql', '2020-07-21 16:01:00.876573+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-07-21 16:01:00.887088+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1542801241-initial-team-migration.sql', '2020-07-21 16:01:00.898301+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1547596236-initial-team-member-migration.js', '2020-07-21 16:01:00.954317+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548205275-move-members.js', '2020-07-21 16:01:01.009825+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548205276-simplify-object.js', '2020-07-21 16:01:01.025532+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548328420-add-alias-migration.js', '2020-07-21 16:01:01.068783+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-07-21 16:01:01.078878+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1580908536-add-identities.sql', '2020-07-21 16:01:01.092107+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1581371297-migrate-users-to-identities.js', '2020-07-21 16:01:01.107057+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1581450834-manuscript.sql', '2020-07-21 16:01:01.118725+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1582930582-drop-fragments-and-collections.js', '2020-07-21 16:01:01.12668+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585323910-add-channels.sql', '2020-07-21 16:01:01.14497+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585344885-add-messages.sql', '2020-07-21 16:01:01.15657+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585513226-add-profile-pic.sql', '2020-07-21 16:01:01.162443+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities-constraint.sql', '2020-07-21 16:01:01.17359+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-08-12 14:59:10.439327+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-08-12 14:59:10.452184+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-08-12 14:59:10.463129+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1580908536-add-identities.sql', '2020-08-12 14:59:10.477935+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581371297-migrate-users-to-identities.js', '2020-08-12 14:59:10.499722+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581450834-manuscript.sql', '2020-08-12 14:59:10.509542+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1582930582-drop-fragments-and-collections.js', '2020-08-12 14:59:10.519573+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585323910-add-channels.sql', '2020-08-12 14:59:10.533012+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585344885-add-messages.sql', '2020-08-12 14:59:10.544214+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585513226-add-profile-pic.sql', '2020-08-12 14:59:10.549847+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities-constraint.sql', '2020-08-12 14:59:10.558714+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830547-review.sql', '2020-08-12 14:59:10.571826+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-add-review-comments.sql', '2020-08-12 14:59:10.583466+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-initial-team-migration.sql', '2020-08-12 14:59:10.611618+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020-08-12 14:59:10.627188+02');
+
+
+--
+-- Data for Name: review_comments; Type: TABLE DATA; Schema: public; Owner: test
+--
+
 
 
 --
@@ -488,13 +501,6 @@ INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities
 -- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.381+02', '2020-07-24 16:43:03.114+02', NULL, NULL, '0000000294294446', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser1.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-07-24 16:43:15.46+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-07-24 16:43:26.378+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-07-24 16:43:43.943+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', true);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-07-24 16:49:06.488+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', true);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-07-24 16:44:59.306+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', true);
 
 
 --
@@ -585,6 +591,14 @@ ALTER TABLE ONLY public.migrations
     ADD CONSTRAINT migrations_pkey PRIMARY KEY (id);
 
 
+--
+-- Name: review_comments review_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_pkey PRIMARY KEY (id);
+
+
 --
 -- Name: reviews reviews_pkey; Type: CONSTRAINT; Schema: public; Owner: test
 --
@@ -690,17 +704,17 @@ CREATE UNIQUE INDEX is_default_idx ON public.identities USING btree (is_default,
 
 
 --
--- Name: team_members_team_id_user_id_index; Type: INDEX; Schema: public; Owner: test
+-- Name: team_members_team_id_user_id_idx; Type: INDEX; Schema: public; Owner: test
 --
 
-CREATE INDEX team_members_team_id_user_id_index ON public.team_members USING btree (team_id, user_id);
+CREATE INDEX team_members_team_id_user_id_idx ON public.team_members USING btree (team_id, user_id);
 
 
 --
--- Name: teams_object_id_object_type_index; Type: INDEX; Schema: public; Owner: test
+-- Name: teams_manuscript_id_idx; Type: INDEX; Schema: public; Owner: test
 --
 
-CREATE INDEX teams_object_id_object_type_index ON public.teams USING btree (object_id, object_type);
+CREATE INDEX teams_manuscript_id_idx ON public.teams USING btree (manuscript_id);
 
 
 --
@@ -728,11 +742,19 @@ ALTER TABLE ONLY public.channels
 
 
 --
--- Name: channels channels_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: files files_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
-ALTER TABLE ONLY public.channels
-    ADD CONSTRAINT channels_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id);
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- Name: files files_review_comment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_review_comment_id_fkey FOREIGN KEY (review_comment_id) REFERENCES public.review_comments(id) ON DELETE CASCADE;
 
 
 --
@@ -759,6 +781,30 @@ ALTER TABLE ONLY public.messages
     ADD CONSTRAINT messages_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
 
 
+--
+-- Name: review_comments review_comments_review_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_review_id_fkey FOREIGN KEY (review_id) REFERENCES public.reviews(id) ON DELETE CASCADE;
+
+
+--
+-- Name: review_comments review_comments_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL;
+
+
+--
+-- Name: reviews reviews_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.reviews
+    ADD CONSTRAINT reviews_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
 --
 -- Name: identities sidentities_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
@@ -768,30 +814,55 @@ ALTER TABLE ONLY public.identities
 
 
 --
--- Name: team_members team_members_alias_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_alias_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_alias_id_foreign FOREIGN KEY (alias_id) REFERENCES public.aliases(id);
+    ADD CONSTRAINT team_members_alias_id_fkey FOREIGN KEY (alias_id) REFERENCES public.aliases(id);
 
 
 --
--- Name: team_members team_members_team_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_team_id_foreign FOREIGN KEY (team_id) REFERENCES public.teams(id) ON UPDATE CASCADE ON DELETE CASCADE;
+    ADD CONSTRAINT team_members_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id) ON UPDATE CASCADE ON DELETE CASCADE;
 
 
 --
--- Name: team_members team_members_user_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_user_id_foreign FOREIGN KEY (user_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE CASCADE;
+    ADD CONSTRAINT team_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: teams teams_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.teams
+    ADD CONSTRAINT teams_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
 
 
 --
 -- PostgreSQL database dump complete
 --
 
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.381+02', '2020-07-24 16:43:03.114+02', NULL, NULL, '0000000294294446', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser1.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-07-24 16:43:15.46+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-07-24 16:43:26.378+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-07-24 16:43:43.943+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-07-24 16:49:06.488+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-07-24 16:44:59.306+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', true);
+
+
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('d341a633-cdce-4a7f-a9ad-5afc03cd0dd1', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.741+02', '2020-07-21 16:17:25.87+02', 'orcid', '0000-0002-0564-2016', 'Emily Clay', NULL, '{"accessToken": "079a1165-31e5-4b59-9a99-d80ff7a21ebf", "refreshToken": "ccadc737-defc-419e-823b-a9f3673848ba"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('bcda196e-765a-42c8-94da-ca2e43b80f96', '3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.721+02', '2020-07-21 16:33:26.742+02', 'orcid', '0000-0002-5641-5729', 'Sinead Sullivan', NULL, '{"accessToken": "ef1ed3ec-8371-41b2-a136-fd196ae52a72", "refreshToken": "6972dace-d9a6-4cd3-a2ad-ec7eb3e457c7"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('4af83984-6359-47c5-a075-5ddfa9c555d9', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.127+02', '2020-07-21 16:35:07.104+02', 'orcid', '0000-0002-7645-9921', 'Sherry Crofoot', NULL, '{"accessToken": "2ad4e130-0775-4e13-87fb-8e8f5a0570ae", "refreshToken": "159933d9-2020-4c02-bdfb-163af41017dc"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('acfa1777-0aec-4fe1-bc16-92bb9d19e884', '85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.384+02', '2020-07-21 16:35:39.358+02', 'orcid', '0000-0002-9429-4446', 'Elaine Barnes', NULL, '{"accessToken": "dcf07bc7-e59c-41b3-9ce0-924ac20aeeea", "refreshToken": "ae49d6a1-8e62-419d-8767-4a3ec22c1950"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('88c85115-d83c-42d7-a1a1-0139827977da', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.975+02', '2020-07-21 16:36:26.059+02', 'orcid', '0000-0001-5956-7341', 'Gale Davis', NULL, '{"accessToken": "3e9f6f6c-7cc0-4afa-9fdf-6ed377c36aad", "refreshToken": "80b1e911-df97-43f1-9f11-17b61913f6d7"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('049f91da-c84e-4b80-be2e-6e0cfca7a136', '231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.611+02', '2020-07-22 14:18:37.745+02', 'orcid', '0000-0003-2536-230X', 'Test Account', NULL, '{"accessToken": "eb551178-79e5-4189-8c5f-0a553092a9b5", "refreshToken": "4506fa5f-bd77-4867-afb4-0b07ea5302d6"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('2fb8359c-239c-43fa-91f5-1ff2058272a6', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.604+02', '2020-07-24 15:21:55.7+02', 'orcid', '0000-0003-1838-2441', 'Joanne Pilger', NULL, '{"accessToken": "842de329-ef16-4461-b83b-e8fe57238904", "refreshToken": "524fbdc5-9c67-4b4c-af17-2ce4cf294e88"}', true);
+
diff --git a/cypress/dumps/reviewers_invited.sql b/cypress/dumps/reviewers_invited.sql
index 0af47e77a00e7b820c4706585abb16bfeffe0d1f..3733f35db91d178c49e70b354276c2dedc16d5b7 100644
--- a/cypress/dumps/reviewers_invited.sql
+++ b/cypress/dumps/reviewers_invited.sql
@@ -145,9 +145,9 @@ CREATE TABLE public.aliases (
     id uuid NOT NULL,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
     updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
-    name character varying(255),
-    email character varying(255),
-    aff character varying(255)
+    name text,
+    email text,
+    aff text
 );
 
 
@@ -175,7 +175,6 @@ ALTER TABLE public.channel_members OWNER TO test;
 CREATE TABLE public.channels (
     id uuid NOT NULL,
     manuscript_id uuid,
-    team_id uuid,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
     updated timestamp with time zone,
     topic text,
@@ -205,15 +204,16 @@ CREATE TABLE public.files (
     id uuid NOT NULL,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
     updated timestamp with time zone,
-    object text,
-    object_id uuid,
     label text,
     file_type text,
     filename text,
     url text,
     mime_type text,
     size integer,
-    type text NOT NULL
+    type text NOT NULL,
+    manuscript_id uuid,
+    review_comment_id uuid,
+    CONSTRAINT exactly_one_file_owner CHECK (((((manuscript_id IS NOT NULL))::integer + ((review_comment_id IS NOT NULL))::integer) = 1))
 );
 
 
@@ -289,6 +289,24 @@ CREATE TABLE public.migrations (
 
 ALTER TABLE public.migrations OWNER TO test;
 
+--
+-- Name: review_comments; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.review_comments (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    review_id uuid,
+    user_id uuid,
+    content text,
+    comment_type text,
+    type text
+);
+
+
+ALTER TABLE public.review_comments OWNER TO test;
+
 --
 -- Name: reviews; Type: TABLE; Schema: public; Owner: test
 --
@@ -299,7 +317,6 @@ CREATE TABLE public.reviews (
     updated timestamp with time zone,
     recommendation text,
     is_decision boolean DEFAULT false,
-    comments jsonb,
     user_id uuid,
     manuscript_id uuid,
     type text NOT NULL
@@ -335,11 +352,11 @@ CREATE TABLE public.teams (
     updated timestamp with time zone,
     name text,
     role text NOT NULL,
+    members jsonb,
     owners jsonb,
     global boolean,
     type text NOT NULL,
-    object_id uuid,
-    object_type character varying(255)
+    manuscript_id uuid
 );
 
 
@@ -403,8 +420,8 @@ INSERT INTO pgboss.version (version) VALUES ('11');
 -- Data for Name: channels; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.channels (id, manuscript_id, team_id, created, updated, topic, type) VALUES ('466f49f5-f4ca-46ca-82ca-693212ca9ba2', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', NULL, '2020-07-28 00:41:34.01+02', '2020-07-28 00:41:34.01+02', 'Editorial discussion', 'editorial');
-INSERT INTO public.channels (id, manuscript_id, team_id, created, updated, topic, type) VALUES ('d24525e7-f36e-4f5e-b0cb-e05ef0ea9062', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', NULL, '2020-07-28 00:41:34.011+02', '2020-07-28 00:41:34.011+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('afd95c06-011b-46e3-9985-964283d51c4a', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('75540565-6619-495f-a8c5-d6bf11ece72d', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Editorial discussion', 'editorial');
 
 
 --
@@ -436,7 +453,7 @@ INSERT INTO public.identities (id, user_id, created, updated, type, identifier,
 -- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('fb2534c6-ecbb-4846-a61d-609dc119e9b0', '2020-07-28 00:41:33.983+02', '2020-07-28 00:42:03.231+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzk", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
+INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.9+02', '2020-08-12 16:17:12.499+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keyword", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
 
 
 --
@@ -449,24 +466,27 @@ INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, s
 -- Data for Name: migrations; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-07-21 16:01:00.856209+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-files.sql', '2020-07-21 16:01:00.866487+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-review.sql', '2020-07-21 16:01:00.876573+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-07-21 16:01:00.887088+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1542801241-initial-team-migration.sql', '2020-07-21 16:01:00.898301+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1547596236-initial-team-member-migration.js', '2020-07-21 16:01:00.954317+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548205275-move-members.js', '2020-07-21 16:01:01.009825+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548205276-simplify-object.js', '2020-07-21 16:01:01.025532+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548328420-add-alias-migration.js', '2020-07-21 16:01:01.068783+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-07-21 16:01:01.078878+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1580908536-add-identities.sql', '2020-07-21 16:01:01.092107+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1581371297-migrate-users-to-identities.js', '2020-07-21 16:01:01.107057+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1581450834-manuscript.sql', '2020-07-21 16:01:01.118725+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1582930582-drop-fragments-and-collections.js', '2020-07-21 16:01:01.12668+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585323910-add-channels.sql', '2020-07-21 16:01:01.14497+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585344885-add-messages.sql', '2020-07-21 16:01:01.15657+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585513226-add-profile-pic.sql', '2020-07-21 16:01:01.162443+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities-constraint.sql', '2020-07-21 16:01:01.17359+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-08-12 14:59:10.439327+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-08-12 14:59:10.452184+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-08-12 14:59:10.463129+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1580908536-add-identities.sql', '2020-08-12 14:59:10.477935+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581371297-migrate-users-to-identities.js', '2020-08-12 14:59:10.499722+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581450834-manuscript.sql', '2020-08-12 14:59:10.509542+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1582930582-drop-fragments-and-collections.js', '2020-08-12 14:59:10.519573+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585323910-add-channels.sql', '2020-08-12 14:59:10.533012+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585344885-add-messages.sql', '2020-08-12 14:59:10.544214+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585513226-add-profile-pic.sql', '2020-08-12 14:59:10.549847+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities-constraint.sql', '2020-08-12 14:59:10.558714+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830547-review.sql', '2020-08-12 14:59:10.571826+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-add-review-comments.sql', '2020-08-12 14:59:10.583466+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-initial-team-migration.sql', '2020-08-12 14:59:10.611618+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020-08-12 14:59:10.627188+02');
+
+
+--
+-- Data for Name: review_comments; Type: TABLE DATA; Schema: public; Owner: test
+--
+
 
 
 --
@@ -479,20 +499,20 @@ INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities
 -- Data for Name: team_members; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('af28eb3b-495e-485d-879d-4d609e146dc6', '2020-07-28 00:41:34.024+02', '2020-07-28 00:41:34.024+02', NULL, '0ecdfc46-2400-4818-8623-ee29ad2102cf', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('39066380-96b2-408e-8329-4ff3833f06be', '2020-07-28 00:42:07.541+02', '2020-07-28 00:42:07.541+02', NULL, '6eb1044b-1d8a-4d07-a84d-a167f152a5bf', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('9927026f-4ffa-4805-bc00-cb9b097b7df4', '2020-07-28 00:45:13.354+02', '2020-07-28 00:45:13.354+02', 'invited', 'b107739f-9c14-4f89-9303-229dbfbd2140', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('e69ca277-c295-44aa-a657-af0c7437840e', '2020-07-28 00:45:14.037+02', '2020-07-28 00:45:14.037+02', 'invited', 'b107739f-9c14-4f89-9303-229dbfbd2140', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('2e64054b-6a0a-4768-9b37-8638b618141e', '2020-07-28 00:45:14.852+02', '2020-07-28 00:45:14.852+02', 'invited', 'b107739f-9c14-4f89-9303-229dbfbd2140', '85e1300e-003c-4e96-987b-23812f902477', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('6505126d-2ecd-498b-a27d-18eafbdbc8a6', '2020-08-12 16:16:44.923+02', '2020-08-12 16:16:44.923+02', NULL, 'c31e3116-6176-45b5-b52c-6f7cbfc86007', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('031ae180-e94e-458d-86b7-f2dc1d456e2e', '2020-08-12 16:17:14.795+02', '2020-08-12 16:17:14.795+02', NULL, 'bf800912-56c4-44eb-b425-64e51a9824fe', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('c4ac4fc0-3fae-43b0-89ee-bf7f8fd9885f', '2020-08-12 16:17:43.262+02', '2020-08-12 16:17:43.262+02', 'invited', '0719d132-bb6c-49d9-9122-7911a48cfd60', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('69e896c6-0fdb-421d-a638-1ef7670c03c9', '2020-08-12 16:17:43.884+02', '2020-08-12 16:17:43.884+02', 'invited', '0719d132-bb6c-49d9-9122-7911a48cfd60', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('12c33ef5-4fc3-4d48-8309-a90c0461113c', '2020-08-12 16:17:44.547+02', '2020-08-12 16:17:44.547+02', 'invited', '0719d132-bb6c-49d9-9122-7911a48cfd60', '85e1300e-003c-4e96-987b-23812f902477', NULL);
 
 
 --
 -- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.teams (id, created, updated, name, role, owners, global, type, object_id, object_type) VALUES ('0ecdfc46-2400-4818-8623-ee29ad2102cf', '2020-07-28 00:41:34.01+02', '2020-07-28 00:41:34.01+02', 'Author', 'author', NULL, NULL, 'team', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', 'Manuscript');
-INSERT INTO public.teams (id, created, updated, name, role, owners, global, type, object_id, object_type) VALUES ('6eb1044b-1d8a-4d07-a84d-a167f152a5bf', '2020-07-28 00:42:07.533+02', '2020-07-28 00:42:07.533+02', 'Senior Editor', 'seniorEditor', NULL, NULL, 'team', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', 'Manuscript');
-INSERT INTO public.teams (id, created, updated, name, role, owners, global, type, object_id, object_type) VALUES ('b107739f-9c14-4f89-9303-229dbfbd2140', '2020-07-28 00:45:13.348+02', '2020-07-28 00:45:13.348+02', 'Reviewers', 'reviewer', NULL, NULL, 'team', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', 'Manuscript');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('c31e3116-6176-45b5-b52c-6f7cbfc86007', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Author', 'author', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('bf800912-56c4-44eb-b425-64e51a9824fe', '2020-08-12 16:17:14.789+02', '2020-08-12 16:17:14.789+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('0719d132-bb6c-49d9-9122-7911a48cfd60', '2020-08-12 16:17:43.258+02', '2020-08-12 16:17:43.258+02', 'Reviewers', 'reviewer', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
 
 
 --
@@ -503,9 +523,9 @@ INSERT INTO public.users (id, created, updated, admin, email, username, password
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-07-24 16:43:43.943+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', true);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-07-24 16:44:59.306+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', true);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-07-28 00:42:05.08+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-07-28 00:42:07.665+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-07-28 00:45:11.921+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-12 16:17:13.598+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-12 16:17:14.868+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-12 16:17:41.916+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
 
 
 --
@@ -596,6 +616,14 @@ ALTER TABLE ONLY public.migrations
     ADD CONSTRAINT migrations_pkey PRIMARY KEY (id);
 
 
+--
+-- Name: review_comments review_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_pkey PRIMARY KEY (id);
+
+
 --
 -- Name: reviews reviews_pkey; Type: CONSTRAINT; Schema: public; Owner: test
 --
@@ -701,17 +729,17 @@ CREATE UNIQUE INDEX is_default_idx ON public.identities USING btree (is_default,
 
 
 --
--- Name: team_members_team_id_user_id_index; Type: INDEX; Schema: public; Owner: test
+-- Name: team_members_team_id_user_id_idx; Type: INDEX; Schema: public; Owner: test
 --
 
-CREATE INDEX team_members_team_id_user_id_index ON public.team_members USING btree (team_id, user_id);
+CREATE INDEX team_members_team_id_user_id_idx ON public.team_members USING btree (team_id, user_id);
 
 
 --
--- Name: teams_object_id_object_type_index; Type: INDEX; Schema: public; Owner: test
+-- Name: teams_manuscript_id_idx; Type: INDEX; Schema: public; Owner: test
 --
 
-CREATE INDEX teams_object_id_object_type_index ON public.teams USING btree (object_id, object_type);
+CREATE INDEX teams_manuscript_id_idx ON public.teams USING btree (manuscript_id);
 
 
 --
@@ -739,11 +767,19 @@ ALTER TABLE ONLY public.channels
 
 
 --
--- Name: channels channels_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: files files_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
-ALTER TABLE ONLY public.channels
-    ADD CONSTRAINT channels_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id);
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- Name: files files_review_comment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_review_comment_id_fkey FOREIGN KEY (review_comment_id) REFERENCES public.review_comments(id) ON DELETE CASCADE;
 
 
 --
@@ -770,6 +806,30 @@ ALTER TABLE ONLY public.messages
     ADD CONSTRAINT messages_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
 
 
+--
+-- Name: review_comments review_comments_review_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_review_id_fkey FOREIGN KEY (review_id) REFERENCES public.reviews(id) ON DELETE CASCADE;
+
+
+--
+-- Name: review_comments review_comments_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL;
+
+
+--
+-- Name: reviews reviews_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.reviews
+    ADD CONSTRAINT reviews_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
 --
 -- Name: identities sidentities_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
@@ -779,27 +839,35 @@ ALTER TABLE ONLY public.identities
 
 
 --
--- Name: team_members team_members_alias_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_alias_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_alias_id_foreign FOREIGN KEY (alias_id) REFERENCES public.aliases(id);
+    ADD CONSTRAINT team_members_alias_id_fkey FOREIGN KEY (alias_id) REFERENCES public.aliases(id);
 
 
 --
--- Name: team_members team_members_team_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_team_id_foreign FOREIGN KEY (team_id) REFERENCES public.teams(id) ON UPDATE CASCADE ON DELETE CASCADE;
+    ADD CONSTRAINT team_members_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id) ON UPDATE CASCADE ON DELETE CASCADE;
 
 
 --
--- Name: team_members team_members_user_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_user_id_foreign FOREIGN KEY (user_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE CASCADE;
+    ADD CONSTRAINT team_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: teams teams_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.teams
+    ADD CONSTRAINT teams_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
 
 
 --
diff --git a/cypress/dumps/senior_editor_assigned.sql b/cypress/dumps/senior_editor_assigned.sql
index 1d2b7ce072f2500e08b12e58db79182c09bfbebf..31e0f3e7492017a2e62177a8d3ce55f58638e59f 100644
--- a/cypress/dumps/senior_editor_assigned.sql
+++ b/cypress/dumps/senior_editor_assigned.sql
@@ -145,9 +145,9 @@ CREATE TABLE public.aliases (
     id uuid NOT NULL,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
     updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
-    name character varying(255),
-    email character varying(255),
-    aff character varying(255)
+    name text,
+    email text,
+    aff text
 );
 
 
@@ -175,7 +175,6 @@ ALTER TABLE public.channel_members OWNER TO test;
 CREATE TABLE public.channels (
     id uuid NOT NULL,
     manuscript_id uuid,
-    team_id uuid,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
     updated timestamp with time zone,
     topic text,
@@ -205,15 +204,16 @@ CREATE TABLE public.files (
     id uuid NOT NULL,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
     updated timestamp with time zone,
-    object text,
-    object_id uuid,
     label text,
     file_type text,
     filename text,
     url text,
     mime_type text,
     size integer,
-    type text NOT NULL
+    type text NOT NULL,
+    manuscript_id uuid,
+    review_comment_id uuid,
+    CONSTRAINT exactly_one_file_owner CHECK (((((manuscript_id IS NOT NULL))::integer + ((review_comment_id IS NOT NULL))::integer) = 1))
 );
 
 
@@ -289,6 +289,24 @@ CREATE TABLE public.migrations (
 
 ALTER TABLE public.migrations OWNER TO test;
 
+--
+-- Name: review_comments; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.review_comments (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    review_id uuid,
+    user_id uuid,
+    content text,
+    comment_type text,
+    type text
+);
+
+
+ALTER TABLE public.review_comments OWNER TO test;
+
 --
 -- Name: reviews; Type: TABLE; Schema: public; Owner: test
 --
@@ -299,7 +317,6 @@ CREATE TABLE public.reviews (
     updated timestamp with time zone,
     recommendation text,
     is_decision boolean DEFAULT false,
-    comments jsonb,
     user_id uuid,
     manuscript_id uuid,
     type text NOT NULL
@@ -335,11 +352,11 @@ CREATE TABLE public.teams (
     updated timestamp with time zone,
     name text,
     role text NOT NULL,
+    members jsonb,
     owners jsonb,
     global boolean,
     type text NOT NULL,
-    object_id uuid,
-    object_type character varying(255)
+    manuscript_id uuid
 );
 
 
@@ -403,8 +420,8 @@ INSERT INTO pgboss.version (version) VALUES ('11');
 -- Data for Name: channels; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.channels (id, manuscript_id, team_id, created, updated, topic, type) VALUES ('466f49f5-f4ca-46ca-82ca-693212ca9ba2', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', NULL, '2020-07-28 00:41:34.01+02', '2020-07-28 00:41:34.01+02', 'Editorial discussion', 'editorial');
-INSERT INTO public.channels (id, manuscript_id, team_id, created, updated, topic, type) VALUES ('d24525e7-f36e-4f5e-b0cb-e05ef0ea9062', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', NULL, '2020-07-28 00:41:34.011+02', '2020-07-28 00:41:34.011+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('8be9c91e-10f8-42c1-9d98-429ffb7164a0', 'a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('89848abe-a992-45e4-ace7-36e9c64c4162', 'a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Editorial discussion', 'editorial');
 
 
 --
@@ -417,6 +434,7 @@ INSERT INTO public.channels (id, manuscript_id, team_id, created, updated, topic
 -- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: test
 --
 
+INSERT INTO public.files (id, created, updated, label, file_type, filename, url, mime_type, size, type, manuscript_id, review_comment_id) VALUES ('66070eaf-6ee1-475f-98ba-774843f1238e', '2020-08-12 16:35:19.664+02', '2020-08-12 16:35:19.664+02', NULL, 'supplementary', 'test-pdf.pdf', '/static/uploads/bfc74d5e3c0b8bf97cd5a522bfdb3c87.pdf', 'application/pdf', 142400, 'file', 'a696348d-b226-4703-aad1-ff64ddf30d17', NULL);
 
 
 --
@@ -436,7 +454,7 @@ INSERT INTO public.identities (id, user_id, created, updated, type, identifier,
 -- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('fb2534c6-ecbb-4846-a61d-609dc119e9b0', '2020-07-28 00:41:33.983+02', '2020-07-28 00:42:03.231+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzk", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
+INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.371+02', '2020-08-12 16:35:28.918+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
 
 
 --
@@ -449,24 +467,27 @@ INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, s
 -- Data for Name: migrations; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-07-21 16:01:00.856209+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-files.sql', '2020-07-21 16:01:00.866487+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-review.sql', '2020-07-21 16:01:00.876573+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-07-21 16:01:00.887088+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1542801241-initial-team-migration.sql', '2020-07-21 16:01:00.898301+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1547596236-initial-team-member-migration.js', '2020-07-21 16:01:00.954317+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548205275-move-members.js', '2020-07-21 16:01:01.009825+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548205276-simplify-object.js', '2020-07-21 16:01:01.025532+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548328420-add-alias-migration.js', '2020-07-21 16:01:01.068783+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-07-21 16:01:01.078878+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1580908536-add-identities.sql', '2020-07-21 16:01:01.092107+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1581371297-migrate-users-to-identities.js', '2020-07-21 16:01:01.107057+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1581450834-manuscript.sql', '2020-07-21 16:01:01.118725+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1582930582-drop-fragments-and-collections.js', '2020-07-21 16:01:01.12668+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585323910-add-channels.sql', '2020-07-21 16:01:01.14497+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585344885-add-messages.sql', '2020-07-21 16:01:01.15657+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585513226-add-profile-pic.sql', '2020-07-21 16:01:01.162443+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities-constraint.sql', '2020-07-21 16:01:01.17359+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-08-12 14:59:10.439327+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-08-12 14:59:10.452184+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-08-12 14:59:10.463129+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1580908536-add-identities.sql', '2020-08-12 14:59:10.477935+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581371297-migrate-users-to-identities.js', '2020-08-12 14:59:10.499722+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581450834-manuscript.sql', '2020-08-12 14:59:10.509542+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1582930582-drop-fragments-and-collections.js', '2020-08-12 14:59:10.519573+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585323910-add-channels.sql', '2020-08-12 14:59:10.533012+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585344885-add-messages.sql', '2020-08-12 14:59:10.544214+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585513226-add-profile-pic.sql', '2020-08-12 14:59:10.549847+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities-constraint.sql', '2020-08-12 14:59:10.558714+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830547-review.sql', '2020-08-12 14:59:10.571826+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-add-review-comments.sql', '2020-08-12 14:59:10.583466+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-initial-team-migration.sql', '2020-08-12 14:59:10.611618+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020-08-12 14:59:10.627188+02');
+
+
+--
+-- Data for Name: review_comments; Type: TABLE DATA; Schema: public; Owner: test
+--
+
 
 
 --
@@ -479,16 +500,16 @@ INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities
 -- Data for Name: team_members; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('af28eb3b-495e-485d-879d-4d609e146dc6', '2020-07-28 00:41:34.024+02', '2020-07-28 00:41:34.024+02', NULL, '0ecdfc46-2400-4818-8623-ee29ad2102cf', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('39066380-96b2-408e-8329-4ff3833f06be', '2020-07-28 00:42:07.541+02', '2020-07-28 00:42:07.541+02', NULL, '6eb1044b-1d8a-4d07-a84d-a167f152a5bf', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('d80380a6-c741-4e3c-9933-126cf368c43b', '2020-08-12 16:35:01.381+02', '2020-08-12 16:35:01.381+02', NULL, 'd5dc5913-db33-4576-a129-3c90321464f6', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('a58d3e82-54b7-4946-8ff8-116d84cbc065', '2020-08-12 16:36:01.535+02', '2020-08-12 16:36:01.535+02', NULL, 'd7cd46ee-8d79-47c1-9c06-da5dfd464075', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
 
 
 --
 -- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.teams (id, created, updated, name, role, owners, global, type, object_id, object_type) VALUES ('0ecdfc46-2400-4818-8623-ee29ad2102cf', '2020-07-28 00:41:34.01+02', '2020-07-28 00:41:34.01+02', 'Author', 'author', NULL, NULL, 'team', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', 'Manuscript');
-INSERT INTO public.teams (id, created, updated, name, role, owners, global, type, object_id, object_type) VALUES ('6eb1044b-1d8a-4d07-a84d-a167f152a5bf', '2020-07-28 00:42:07.533+02', '2020-07-28 00:42:07.533+02', 'Senior Editor', 'seniorEditor', NULL, NULL, 'team', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', 'Manuscript');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('d5dc5913-db33-4576-a129-3c90321464f6', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Author', 'author', NULL, NULL, NULL, 'team', 'a696348d-b226-4703-aad1-ff64ddf30d17');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('d7cd46ee-8d79-47c1-9c06-da5dfd464075', '2020-08-12 16:36:01.513+02', '2020-08-12 16:36:01.513+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', 'a696348d-b226-4703-aad1-ff64ddf30d17');
 
 
 --
@@ -499,9 +520,9 @@ INSERT INTO public.users (id, created, updated, admin, email, username, password
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-07-24 16:43:43.943+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', true);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-07-24 16:44:59.306+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', true);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-07-28 00:42:05.08+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-07-28 00:42:07.665+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-07-28 00:42:08.153+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-12 16:35:01.383+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-12 16:36:01.679+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-12 16:36:02.11+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
 
 
 --
@@ -592,6 +613,14 @@ ALTER TABLE ONLY public.migrations
     ADD CONSTRAINT migrations_pkey PRIMARY KEY (id);
 
 
+--
+-- Name: review_comments review_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_pkey PRIMARY KEY (id);
+
+
 --
 -- Name: reviews reviews_pkey; Type: CONSTRAINT; Schema: public; Owner: test
 --
@@ -697,17 +726,17 @@ CREATE UNIQUE INDEX is_default_idx ON public.identities USING btree (is_default,
 
 
 --
--- Name: team_members_team_id_user_id_index; Type: INDEX; Schema: public; Owner: test
+-- Name: team_members_team_id_user_id_idx; Type: INDEX; Schema: public; Owner: test
 --
 
-CREATE INDEX team_members_team_id_user_id_index ON public.team_members USING btree (team_id, user_id);
+CREATE INDEX team_members_team_id_user_id_idx ON public.team_members USING btree (team_id, user_id);
 
 
 --
--- Name: teams_object_id_object_type_index; Type: INDEX; Schema: public; Owner: test
+-- Name: teams_manuscript_id_idx; Type: INDEX; Schema: public; Owner: test
 --
 
-CREATE INDEX teams_object_id_object_type_index ON public.teams USING btree (object_id, object_type);
+CREATE INDEX teams_manuscript_id_idx ON public.teams USING btree (manuscript_id);
 
 
 --
@@ -735,11 +764,19 @@ ALTER TABLE ONLY public.channels
 
 
 --
--- Name: channels channels_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: files files_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
-ALTER TABLE ONLY public.channels
-    ADD CONSTRAINT channels_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id);
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- Name: files files_review_comment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_review_comment_id_fkey FOREIGN KEY (review_comment_id) REFERENCES public.review_comments(id) ON DELETE CASCADE;
 
 
 --
@@ -766,6 +803,30 @@ ALTER TABLE ONLY public.messages
     ADD CONSTRAINT messages_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
 
 
+--
+-- Name: review_comments review_comments_review_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_review_id_fkey FOREIGN KEY (review_id) REFERENCES public.reviews(id) ON DELETE CASCADE;
+
+
+--
+-- Name: review_comments review_comments_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL;
+
+
+--
+-- Name: reviews reviews_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.reviews
+    ADD CONSTRAINT reviews_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
 --
 -- Name: identities sidentities_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
@@ -775,27 +836,35 @@ ALTER TABLE ONLY public.identities
 
 
 --
--- Name: team_members team_members_alias_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_alias_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_alias_id_foreign FOREIGN KEY (alias_id) REFERENCES public.aliases(id);
+    ADD CONSTRAINT team_members_alias_id_fkey FOREIGN KEY (alias_id) REFERENCES public.aliases(id);
 
 
 --
--- Name: team_members team_members_team_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_team_id_foreign FOREIGN KEY (team_id) REFERENCES public.teams(id) ON UPDATE CASCADE ON DELETE CASCADE;
+    ADD CONSTRAINT team_members_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id) ON UPDATE CASCADE ON DELETE CASCADE;
 
 
 --
--- Name: team_members team_members_user_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_user_id_foreign FOREIGN KEY (user_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE CASCADE;
+    ADD CONSTRAINT team_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: teams teams_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.teams
+    ADD CONSTRAINT teams_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
 
 
 --
diff --git a/cypress/dumps/submission_complete.sql b/cypress/dumps/submission_complete.sql
index b318c1f25ef542335d277f2932a1c39028e6d940..f9966719d506445849d95faa2ceef2eec6ce1069 100644
--- a/cypress/dumps/submission_complete.sql
+++ b/cypress/dumps/submission_complete.sql
@@ -145,9 +145,9 @@ CREATE TABLE public.aliases (
     id uuid NOT NULL,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
     updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
-    name character varying(255),
-    email character varying(255),
-    aff character varying(255)
+    name text,
+    email text,
+    aff text
 );
 
 
@@ -175,7 +175,6 @@ ALTER TABLE public.channel_members OWNER TO test;
 CREATE TABLE public.channels (
     id uuid NOT NULL,
     manuscript_id uuid,
-    team_id uuid,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
     updated timestamp with time zone,
     topic text,
@@ -205,15 +204,16 @@ CREATE TABLE public.files (
     id uuid NOT NULL,
     created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
     updated timestamp with time zone,
-    object text,
-    object_id uuid,
     label text,
     file_type text,
     filename text,
     url text,
     mime_type text,
     size integer,
-    type text NOT NULL
+    type text NOT NULL,
+    manuscript_id uuid,
+    review_comment_id uuid,
+    CONSTRAINT exactly_one_file_owner CHECK (((((manuscript_id IS NOT NULL))::integer + ((review_comment_id IS NOT NULL))::integer) = 1))
 );
 
 
@@ -289,6 +289,24 @@ CREATE TABLE public.migrations (
 
 ALTER TABLE public.migrations OWNER TO test;
 
+--
+-- Name: review_comments; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.review_comments (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    review_id uuid,
+    user_id uuid,
+    content text,
+    comment_type text,
+    type text
+);
+
+
+ALTER TABLE public.review_comments OWNER TO test;
+
 --
 -- Name: reviews; Type: TABLE; Schema: public; Owner: test
 --
@@ -299,7 +317,6 @@ CREATE TABLE public.reviews (
     updated timestamp with time zone,
     recommendation text,
     is_decision boolean DEFAULT false,
-    comments jsonb,
     user_id uuid,
     manuscript_id uuid,
     type text NOT NULL
@@ -335,11 +352,11 @@ CREATE TABLE public.teams (
     updated timestamp with time zone,
     name text,
     role text NOT NULL,
+    members jsonb,
     owners jsonb,
     global boolean,
     type text NOT NULL,
-    object_id uuid,
-    object_type character varying(255)
+    manuscript_id uuid
 );
 
 
@@ -403,8 +420,8 @@ INSERT INTO pgboss.version (version) VALUES ('11');
 -- Data for Name: channels; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.channels (id, manuscript_id, team_id, created, updated, topic, type) VALUES ('466f49f5-f4ca-46ca-82ca-693212ca9ba2', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', NULL, '2020-07-28 00:41:34.01+02', '2020-07-28 00:41:34.01+02', 'Editorial discussion', 'editorial');
-INSERT INTO public.channels (id, manuscript_id, team_id, created, updated, topic, type) VALUES ('d24525e7-f36e-4f5e-b0cb-e05ef0ea9062', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', NULL, '2020-07-28 00:41:34.011+02', '2020-07-28 00:41:34.011+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('8be9c91e-10f8-42c1-9d98-429ffb7164a0', 'a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('89848abe-a992-45e4-ace7-36e9c64c4162', 'a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Editorial discussion', 'editorial');
 
 
 --
@@ -417,6 +434,7 @@ INSERT INTO public.channels (id, manuscript_id, team_id, created, updated, topic
 -- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: test
 --
 
+INSERT INTO public.files (id, created, updated, label, file_type, filename, url, mime_type, size, type, manuscript_id, review_comment_id) VALUES ('66070eaf-6ee1-475f-98ba-774843f1238e', '2020-08-12 16:35:19.664+02', '2020-08-12 16:35:19.664+02', NULL, 'supplementary', 'test-pdf.pdf', '/static/uploads/bfc74d5e3c0b8bf97cd5a522bfdb3c87.pdf', 'application/pdf', 142400, 'file', 'a696348d-b226-4703-aad1-ff64ddf30d17', NULL);
 
 
 --
@@ -436,7 +454,7 @@ INSERT INTO public.identities (id, user_id, created, updated, type, identifier,
 -- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('fb2534c6-ecbb-4846-a61d-609dc119e9b0', '2020-07-28 00:41:33.983+02', '2020-07-28 00:42:03.231+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzk", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
+INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('a696348d-b226-4703-aad1-ff64ddf30d17', '2020-08-12 16:35:01.371+02', '2020-08-12 16:35:28.918+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keywords", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
 
 
 --
@@ -449,24 +467,27 @@ INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, s
 -- Data for Name: migrations; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-07-21 16:01:00.856209+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-files.sql', '2020-07-21 16:01:00.866487+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-review.sql', '2020-07-21 16:01:00.876573+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-07-21 16:01:00.887088+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1542801241-initial-team-migration.sql', '2020-07-21 16:01:00.898301+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1547596236-initial-team-member-migration.js', '2020-07-21 16:01:00.954317+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548205275-move-members.js', '2020-07-21 16:01:01.009825+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548205276-simplify-object.js', '2020-07-21 16:01:01.025532+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1548328420-add-alias-migration.js', '2020-07-21 16:01:01.068783+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-07-21 16:01:01.078878+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1580908536-add-identities.sql', '2020-07-21 16:01:01.092107+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1581371297-migrate-users-to-identities.js', '2020-07-21 16:01:01.107057+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1581450834-manuscript.sql', '2020-07-21 16:01:01.118725+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1582930582-drop-fragments-and-collections.js', '2020-07-21 16:01:01.12668+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585323910-add-channels.sql', '2020-07-21 16:01:01.14497+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585344885-add-messages.sql', '2020-07-21 16:01:01.15657+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1585513226-add-profile-pic.sql', '2020-07-21 16:01:01.162443+02');
-INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities-constraint.sql', '2020-07-21 16:01:01.17359+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-08-12 14:59:10.439327+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-08-12 14:59:10.452184+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-08-12 14:59:10.463129+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1580908536-add-identities.sql', '2020-08-12 14:59:10.477935+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581371297-migrate-users-to-identities.js', '2020-08-12 14:59:10.499722+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581450834-manuscript.sql', '2020-08-12 14:59:10.509542+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1582930582-drop-fragments-and-collections.js', '2020-08-12 14:59:10.519573+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585323910-add-channels.sql', '2020-08-12 14:59:10.533012+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585344885-add-messages.sql', '2020-08-12 14:59:10.544214+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585513226-add-profile-pic.sql', '2020-08-12 14:59:10.549847+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities-constraint.sql', '2020-08-12 14:59:10.558714+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830547-review.sql', '2020-08-12 14:59:10.571826+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-add-review-comments.sql', '2020-08-12 14:59:10.583466+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-initial-team-migration.sql', '2020-08-12 14:59:10.611618+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020-08-12 14:59:10.627188+02');
+
+
+--
+-- Data for Name: review_comments; Type: TABLE DATA; Schema: public; Owner: test
+--
+
 
 
 --
@@ -479,14 +500,14 @@ INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities
 -- Data for Name: team_members; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('af28eb3b-495e-485d-879d-4d609e146dc6', '2020-07-28 00:41:34.024+02', '2020-07-28 00:41:34.024+02', NULL, '0ecdfc46-2400-4818-8623-ee29ad2102cf', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('d80380a6-c741-4e3c-9933-126cf368c43b', '2020-08-12 16:35:01.381+02', '2020-08-12 16:35:01.381+02', NULL, 'd5dc5913-db33-4576-a129-3c90321464f6', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
 
 
 --
 -- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: test
 --
 
-INSERT INTO public.teams (id, created, updated, name, role, owners, global, type, object_id, object_type) VALUES ('0ecdfc46-2400-4818-8623-ee29ad2102cf', '2020-07-28 00:41:34.01+02', '2020-07-28 00:41:34.01+02', 'Author', 'author', NULL, NULL, 'team', 'fb2534c6-ecbb-4846-a61d-609dc119e9b0', 'Manuscript');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('d5dc5913-db33-4576-a129-3c90321464f6', '2020-08-12 16:35:01.376+02', '2020-08-12 16:35:01.376+02', 'Author', 'author', NULL, NULL, NULL, 'team', 'a696348d-b226-4703-aad1-ff64ddf30d17');
 
 
 --
@@ -499,7 +520,7 @@ INSERT INTO public.users (id, created, updated, admin, email, username, password
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-07-24 16:49:06.488+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', true);
 INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-07-24 16:44:59.306+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', true);
-INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-07-28 00:41:34.032+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', true);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-12 16:35:01.383+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', true);
 
 
 --
@@ -590,6 +611,14 @@ ALTER TABLE ONLY public.migrations
     ADD CONSTRAINT migrations_pkey PRIMARY KEY (id);
 
 
+--
+-- Name: review_comments review_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_pkey PRIMARY KEY (id);
+
+
 --
 -- Name: reviews reviews_pkey; Type: CONSTRAINT; Schema: public; Owner: test
 --
@@ -695,17 +724,17 @@ CREATE UNIQUE INDEX is_default_idx ON public.identities USING btree (is_default,
 
 
 --
--- Name: team_members_team_id_user_id_index; Type: INDEX; Schema: public; Owner: test
+-- Name: team_members_team_id_user_id_idx; Type: INDEX; Schema: public; Owner: test
 --
 
-CREATE INDEX team_members_team_id_user_id_index ON public.team_members USING btree (team_id, user_id);
+CREATE INDEX team_members_team_id_user_id_idx ON public.team_members USING btree (team_id, user_id);
 
 
 --
--- Name: teams_object_id_object_type_index; Type: INDEX; Schema: public; Owner: test
+-- Name: teams_manuscript_id_idx; Type: INDEX; Schema: public; Owner: test
 --
 
-CREATE INDEX teams_object_id_object_type_index ON public.teams USING btree (object_id, object_type);
+CREATE INDEX teams_manuscript_id_idx ON public.teams USING btree (manuscript_id);
 
 
 --
@@ -733,11 +762,19 @@ ALTER TABLE ONLY public.channels
 
 
 --
--- Name: channels channels_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: files files_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
-ALTER TABLE ONLY public.channels
-    ADD CONSTRAINT channels_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id);
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- Name: files files_review_comment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_review_comment_id_fkey FOREIGN KEY (review_comment_id) REFERENCES public.review_comments(id) ON DELETE CASCADE;
 
 
 --
@@ -764,6 +801,30 @@ ALTER TABLE ONLY public.messages
     ADD CONSTRAINT messages_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
 
 
+--
+-- Name: review_comments review_comments_review_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_review_id_fkey FOREIGN KEY (review_id) REFERENCES public.reviews(id) ON DELETE CASCADE;
+
+
+--
+-- Name: review_comments review_comments_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL;
+
+
+--
+-- Name: reviews reviews_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.reviews
+    ADD CONSTRAINT reviews_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
 --
 -- Name: identities sidentities_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
@@ -773,27 +834,35 @@ ALTER TABLE ONLY public.identities
 
 
 --
--- Name: team_members team_members_alias_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_alias_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_alias_id_foreign FOREIGN KEY (alias_id) REFERENCES public.aliases(id);
+    ADD CONSTRAINT team_members_alias_id_fkey FOREIGN KEY (alias_id) REFERENCES public.aliases(id);
 
 
 --
--- Name: team_members team_members_team_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_team_id_foreign FOREIGN KEY (team_id) REFERENCES public.teams(id) ON UPDATE CASCADE ON DELETE CASCADE;
+    ADD CONSTRAINT team_members_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id) ON UPDATE CASCADE ON DELETE CASCADE;
 
 
 --
--- Name: team_members team_members_user_id_foreign; Type: FK CONSTRAINT; Schema: public; Owner: test
+-- Name: team_members team_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
 --
 
 ALTER TABLE ONLY public.team_members
-    ADD CONSTRAINT team_members_user_id_foreign FOREIGN KEY (user_id) REFERENCES public.users(id) ON UPDATE CASCADE ON DELETE CASCADE;
+    ADD CONSTRAINT team_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: teams teams_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.teams
+    ADD CONSTRAINT teams_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
 
 
 --
diff --git a/cypress/dumps/three_reviews_completed.sql b/cypress/dumps/three_reviews_completed.sql
new file mode 100644
index 0000000000000000000000000000000000000000..8e7c8bba5d5ed812fb202665671a3580c2784c6c
--- /dev/null
+++ b/cypress/dumps/three_reviews_completed.sql
@@ -0,0 +1,885 @@
+--
+-- PostgreSQL database dump
+--
+
+-- Dumped from database version 10.5
+-- Dumped by pg_dump version 10.5
+
+SET statement_timeout = 0;
+SET lock_timeout = 0;
+SET idle_in_transaction_session_timeout = 0;
+SET client_encoding = 'UTF8';
+SET standard_conforming_strings = on;
+SELECT pg_catalog.set_config('search_path', '', false);
+SET check_function_bodies = false;
+SET client_min_messages = warning;
+SET row_security = off;
+
+--
+-- Name: pgboss; Type: SCHEMA; Schema: -; Owner: test
+--
+
+CREATE SCHEMA pgboss;
+
+
+ALTER SCHEMA pgboss OWNER TO test;
+
+--
+-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: 
+--
+
+CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
+
+
+--
+-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: 
+--
+
+COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
+
+
+--
+-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: 
+--
+
+CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public;
+
+
+--
+-- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: 
+--
+
+COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions';
+
+
+--
+-- Name: job_state; Type: TYPE; Schema: pgboss; Owner: test
+--
+
+CREATE TYPE pgboss.job_state AS ENUM (
+    'created',
+    'retry',
+    'active',
+    'completed',
+    'expired',
+    'cancelled',
+    'failed'
+);
+
+
+ALTER TYPE pgboss.job_state OWNER TO test;
+
+SET default_tablespace = '';
+
+SET default_with_oids = false;
+
+--
+-- Name: archive; Type: TABLE; Schema: pgboss; Owner: test
+--
+
+CREATE TABLE pgboss.archive (
+    id uuid NOT NULL,
+    name text NOT NULL,
+    priority integer NOT NULL,
+    data jsonb,
+    state pgboss.job_state NOT NULL,
+    retrylimit integer NOT NULL,
+    retrycount integer NOT NULL,
+    retrydelay integer NOT NULL,
+    retrybackoff boolean NOT NULL,
+    startafter timestamp with time zone NOT NULL,
+    startedon timestamp with time zone,
+    singletonkey text,
+    singletonon timestamp without time zone,
+    expirein interval NOT NULL,
+    createdon timestamp with time zone NOT NULL,
+    completedon timestamp with time zone,
+    archivedon timestamp with time zone DEFAULT now() NOT NULL
+);
+
+
+ALTER TABLE pgboss.archive OWNER TO test;
+
+--
+-- Name: job; Type: TABLE; Schema: pgboss; Owner: test
+--
+
+CREATE TABLE pgboss.job (
+    id uuid DEFAULT public.gen_random_uuid() NOT NULL,
+    name text NOT NULL,
+    priority integer DEFAULT 0 NOT NULL,
+    data jsonb,
+    state pgboss.job_state DEFAULT 'created'::pgboss.job_state NOT NULL,
+    retrylimit integer DEFAULT 0 NOT NULL,
+    retrycount integer DEFAULT 0 NOT NULL,
+    retrydelay integer DEFAULT 0 NOT NULL,
+    retrybackoff boolean DEFAULT false NOT NULL,
+    startafter timestamp with time zone DEFAULT now() NOT NULL,
+    startedon timestamp with time zone,
+    singletonkey text,
+    singletonon timestamp without time zone,
+    expirein interval DEFAULT '00:15:00'::interval NOT NULL,
+    createdon timestamp with time zone DEFAULT now() NOT NULL,
+    completedon timestamp with time zone
+);
+
+
+ALTER TABLE pgboss.job OWNER TO test;
+
+--
+-- Name: version; Type: TABLE; Schema: pgboss; Owner: test
+--
+
+CREATE TABLE pgboss.version (
+    version text NOT NULL
+);
+
+
+ALTER TABLE pgboss.version OWNER TO test;
+
+--
+-- Name: aliases; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.aliases (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    name text,
+    email text,
+    aff text
+);
+
+
+ALTER TABLE public.aliases OWNER TO test;
+
+--
+-- Name: channel_members; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.channel_members (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    user_id uuid NOT NULL,
+    channel_id uuid NOT NULL
+);
+
+
+ALTER TABLE public.channel_members OWNER TO test;
+
+--
+-- Name: channels; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.channels (
+    id uuid NOT NULL,
+    manuscript_id uuid,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    topic text,
+    type text
+);
+
+
+ALTER TABLE public.channels OWNER TO test;
+
+--
+-- Name: entities; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.entities (
+    id uuid NOT NULL,
+    data jsonb
+);
+
+
+ALTER TABLE public.entities OWNER TO test;
+
+--
+-- Name: files; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.files (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    label text,
+    file_type text,
+    filename text,
+    url text,
+    mime_type text,
+    size integer,
+    type text NOT NULL,
+    manuscript_id uuid,
+    review_comment_id uuid,
+    CONSTRAINT exactly_one_file_owner CHECK (((((manuscript_id IS NOT NULL))::integer + ((review_comment_id IS NOT NULL))::integer) = 1))
+);
+
+
+ALTER TABLE public.files OWNER TO test;
+
+--
+-- Name: identities; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.identities (
+    id uuid NOT NULL,
+    user_id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    type text NOT NULL,
+    identifier text,
+    name text,
+    aff text,
+    oauth jsonb,
+    is_default boolean
+);
+
+
+ALTER TABLE public.identities OWNER TO test;
+
+--
+-- Name: manuscripts; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.manuscripts (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    parent_id uuid,
+    submitter_id uuid,
+    status text,
+    decision text,
+    authors jsonb,
+    suggestions jsonb,
+    meta jsonb,
+    submission jsonb,
+    type text NOT NULL
+);
+
+
+ALTER TABLE public.manuscripts OWNER TO test;
+
+--
+-- Name: messages; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.messages (
+    id uuid NOT NULL,
+    user_id uuid NOT NULL,
+    channel_id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    content text
+);
+
+
+ALTER TABLE public.messages OWNER TO test;
+
+--
+-- Name: migrations; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.migrations (
+    id text NOT NULL,
+    run_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP
+);
+
+
+ALTER TABLE public.migrations OWNER TO test;
+
+--
+-- Name: review_comments; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.review_comments (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    review_id uuid,
+    user_id uuid,
+    content text,
+    comment_type text,
+    type text
+);
+
+
+ALTER TABLE public.review_comments OWNER TO test;
+
+--
+-- Name: reviews; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.reviews (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    recommendation text,
+    is_decision boolean DEFAULT false,
+    user_id uuid,
+    manuscript_id uuid,
+    type text NOT NULL
+);
+
+
+ALTER TABLE public.reviews OWNER TO test;
+
+--
+-- Name: team_members; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.team_members (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    status character varying(255),
+    team_id uuid,
+    user_id uuid,
+    alias_id uuid
+);
+
+
+ALTER TABLE public.team_members OWNER TO test;
+
+--
+-- Name: teams; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.teams (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    name text,
+    role text NOT NULL,
+    members jsonb,
+    owners jsonb,
+    global boolean,
+    type text NOT NULL,
+    manuscript_id uuid
+);
+
+
+ALTER TABLE public.teams OWNER TO test;
+
+--
+-- Name: users; Type: TABLE; Schema: public; Owner: test
+--
+
+CREATE TABLE public.users (
+    id uuid NOT NULL,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    updated timestamp with time zone,
+    admin boolean,
+    email text,
+    username text,
+    password_hash text,
+    teams jsonb,
+    password_reset_token text,
+    password_reset_timestamp timestamp with time zone,
+    type text NOT NULL,
+    profile_picture text,
+    online boolean
+);
+
+
+ALTER TABLE public.users OWNER TO test;
+
+--
+-- Data for Name: archive; Type: TABLE DATA; Schema: pgboss; Owner: test
+--
+
+
+
+--
+-- Data for Name: job; Type: TABLE DATA; Schema: pgboss; Owner: test
+--
+
+
+
+--
+-- Data for Name: version; Type: TABLE DATA; Schema: pgboss; Owner: test
+--
+
+INSERT INTO pgboss.version (version) VALUES ('11');
+
+
+--
+-- Data for Name: aliases; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+
+
+--
+-- Data for Name: channel_members; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+
+
+--
+-- Data for Name: channels; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('afd95c06-011b-46e3-9985-964283d51c4a', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Manuscript discussion', 'all');
+INSERT INTO public.channels (id, manuscript_id, created, updated, topic, type) VALUES ('75540565-6619-495f-a8c5-d6bf11ece72d', '93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Editorial discussion', 'editorial');
+
+
+--
+-- Data for Name: entities; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+
+
+--
+-- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+
+
+--
+-- Data for Name: identities; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('d341a633-cdce-4a7f-a9ad-5afc03cd0dd1', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.741+02', '2020-07-21 16:17:25.87+02', 'orcid', '0000-0002-0564-2016', 'Emily Clay', NULL, '{"accessToken": "079a1165-31e5-4b59-9a99-d80ff7a21ebf", "refreshToken": "ccadc737-defc-419e-823b-a9f3673848ba"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('bcda196e-765a-42c8-94da-ca2e43b80f96', '3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.721+02', '2020-07-21 16:33:26.742+02', 'orcid', '0000-0002-5641-5729', 'Sinead Sullivan', NULL, '{"accessToken": "ef1ed3ec-8371-41b2-a136-fd196ae52a72", "refreshToken": "6972dace-d9a6-4cd3-a2ad-ec7eb3e457c7"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('4af83984-6359-47c5-a075-5ddfa9c555d9', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.127+02', '2020-07-21 16:35:07.104+02', 'orcid', '0000-0002-7645-9921', 'Sherry Crofoot', NULL, '{"accessToken": "2ad4e130-0775-4e13-87fb-8e8f5a0570ae", "refreshToken": "159933d9-2020-4c02-bdfb-163af41017dc"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('acfa1777-0aec-4fe1-bc16-92bb9d19e884', '85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.384+02', '2020-07-21 16:35:39.358+02', 'orcid', '0000-0002-9429-4446', 'Elaine Barnes', NULL, '{"accessToken": "dcf07bc7-e59c-41b3-9ce0-924ac20aeeea", "refreshToken": "ae49d6a1-8e62-419d-8767-4a3ec22c1950"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('88c85115-d83c-42d7-a1a1-0139827977da', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.975+02', '2020-07-21 16:36:26.059+02', 'orcid', '0000-0001-5956-7341', 'Gale Davis', NULL, '{"accessToken": "3e9f6f6c-7cc0-4afa-9fdf-6ed377c36aad", "refreshToken": "80b1e911-df97-43f1-9f11-17b61913f6d7"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('049f91da-c84e-4b80-be2e-6e0cfca7a136', '231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.611+02', '2020-07-22 14:18:37.745+02', 'orcid', '0000-0003-2536-230X', 'Test Account', NULL, '{"accessToken": "eb551178-79e5-4189-8c5f-0a553092a9b5", "refreshToken": "4506fa5f-bd77-4867-afb4-0b07ea5302d6"}', true);
+INSERT INTO public.identities (id, user_id, created, updated, type, identifier, name, aff, oauth, is_default) VALUES ('2fb8359c-239c-43fa-91f5-1ff2058272a6', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.604+02', '2020-07-24 15:21:55.7+02', 'orcid', '0000-0003-1838-2441', 'Joanne Pilger', NULL, '{"accessToken": "842de329-ef16-4461-b83b-e8fe57238904", "refreshToken": "524fbdc5-9c67-4b4c-af17-2ce4cf294e88"}', true);
+
+
+--
+-- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.manuscripts (id, created, updated, parent_id, submitter_id, status, decision, authors, suggestions, meta, submission, type) VALUES ('93735475-08f6-436b-9db3-fe2364b9dbb2', '2020-08-12 16:16:44.9+02', '2020-08-12 16:17:12.499+02', NULL, '027afa6a-edbc-486e-bb31-71e12f8ea1c5', 'submitted', NULL, NULL, NULL, '{"notes": [{"content": "", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions"}], "title": "My URL submission"}', '{"irb": "yes", "name": "Emily Clay", "cover": "This is my cover letter", "links": "https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml", "ethics": "This is my ethics statement", "contact": "emily@example.com", "methods": ["Functional MRI", "Optical Imaging"], "datacode": "This is my data and code availability statement", "humanMRI": "3T", "keywords": "some, keyword", "packages": ["SPM", "FSL"], "subjects": "patients", "suggested": "Erica James, Matthew Matretzky", "objectType": "software", "affiliation": "Example University, Egland", "otherMethods": "Erica James, Matthew Matretzky", "humanMRIother": "7T", "otherPackages": "Jupyter, Stencila", "animal_research_approval": "yes"}', 'Manuscript');
+
+
+--
+-- Data for Name: messages; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+
+
+--
+-- Data for Name: migrations; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-08-12 14:59:10.439327+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-08-12 14:59:10.452184+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-08-12 14:59:10.463129+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1580908536-add-identities.sql', '2020-08-12 14:59:10.477935+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581371297-migrate-users-to-identities.js', '2020-08-12 14:59:10.499722+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1581450834-manuscript.sql', '2020-08-12 14:59:10.509542+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1582930582-drop-fragments-and-collections.js', '2020-08-12 14:59:10.519573+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585323910-add-channels.sql', '2020-08-12 14:59:10.533012+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585344885-add-messages.sql', '2020-08-12 14:59:10.544214+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1585513226-add-profile-pic.sql', '2020-08-12 14:59:10.549847+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1592915682-change-identities-constraint.sql', '2020-08-12 14:59:10.558714+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830547-review.sql', '2020-08-12 14:59:10.571826+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-add-review-comments.sql', '2020-08-12 14:59:10.583466+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596830548-initial-team-migration.sql', '2020-08-12 14:59:10.611618+02');
+INSERT INTO public.migrations (id, run_at) VALUES ('1596838897-files.sql', '2020-08-12 14:59:10.627188+02');
+
+
+--
+-- Data for Name: review_comments; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('f73fdbd8-6fb6-4d7a-a74a-ab2c2c07b572', '2020-08-13 15:09:33.838+02', '2020-08-13 15:09:33.838+02', '5a06fcf3-d368-4f65-917c-1acdb73fcc71', NULL, '<p>Great paper, congratulations! Gale Davis</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('5c86da62-bc7f-4d23-8968-62bd5a56a52b', '2020-08-13 15:09:34.88+02', '2020-08-13 15:09:34.88+02', '5a06fcf3-d368-4f65-917c-1acdb73fcc71', NULL, '<p>This is a very important paper. Gale Davis</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('ef96b249-dab6-4f7e-b24e-571b340b9b41', '2020-08-13 15:09:38.761+02', '2020-08-13 15:09:38.761+02', '7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', NULL, '<p>Great paper, congratulations! Sherry Crofoot</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('04a8a70e-7787-4fd6-bdca-bfac3aa86951', '2020-08-13 15:09:40.155+02', '2020-08-13 15:09:40.155+02', '7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', NULL, '<p>This is a very important paper. Sherry Crofoot</p>', 'confidential', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('7ccda096-07c2-4569-ad0f-5b3574db205e', '2020-08-13 15:09:44.046+02', '2020-08-13 15:09:44.046+02', '3a34e0ef-6695-4268-b901-60de20f9cf4e', NULL, '<p>Great paper, congratulations! Elaine Barnes</p>', 'review', 'ReviewComment');
+INSERT INTO public.review_comments (id, created, updated, review_id, user_id, content, comment_type, type) VALUES ('c8b7d1c9-aef2-47d0-a9ad-c9d46706e79e', '2020-08-13 15:09:45.548+02', '2020-08-13 15:09:45.548+02', '3a34e0ef-6695-4268-b901-60de20f9cf4e', NULL, '<p>This is a very important paper. Elaine Barnes</p>', 'confidential', 'ReviewComment');
+
+
+--
+-- Data for Name: reviews; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('5a06fcf3-d368-4f65-917c-1acdb73fcc71', '2020-08-13 15:09:32.358+02', '2020-08-13 15:09:34.925+02', 'accepted', false, '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('7c6cd2f6-c23e-4902-96ac-df4801ac3d0a', '2020-08-13 15:09:36.501+02', '2020-08-13 15:09:40.159+02', 'accepted', false, '0da0bbec-9261-4706-b990-0c10aa3cc6b4', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
+INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, user_id, manuscript_id, type) VALUES ('3a34e0ef-6695-4268-b901-60de20f9cf4e', '2020-08-13 15:09:41.911+02', '2020-08-13 15:09:45.561+02', 'accepted', false, '85e1300e-003c-4e96-987b-23812f902477', '93735475-08f6-436b-9db3-fe2364b9dbb2', 'Review');
+
+
+--
+-- Data for Name: team_members; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('6505126d-2ecd-498b-a27d-18eafbdbc8a6', '2020-08-12 16:16:44.923+02', '2020-08-12 16:16:44.923+02', NULL, 'c31e3116-6176-45b5-b52c-6f7cbfc86007', '027afa6a-edbc-486e-bb31-71e12f8ea1c5', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('031ae180-e94e-458d-86b7-f2dc1d456e2e', '2020-08-12 16:17:14.795+02', '2020-08-12 16:17:14.795+02', NULL, 'bf800912-56c4-44eb-b425-64e51a9824fe', '1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('c4ac4fc0-3fae-43b0-89ee-bf7f8fd9885f', '2020-08-12 16:17:43.262+02', '2020-08-13 15:09:41.898+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '40e3d054-9ac8-4c0f-84ed-e3c6307662cd', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('69e896c6-0fdb-421d-a638-1ef7670c03c9', '2020-08-12 16:17:43.884+02', '2020-08-13 15:09:41.898+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '0da0bbec-9261-4706-b990-0c10aa3cc6b4', NULL);
+INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('12c33ef5-4fc3-4d48-8309-a90c0461113c', '2020-08-12 16:17:44.547+02', '2020-08-13 15:09:45.782+02', 'completed', '0719d132-bb6c-49d9-9122-7911a48cfd60', '85e1300e-003c-4e96-987b-23812f902477', NULL);
+
+
+--
+-- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('c31e3116-6176-45b5-b52c-6f7cbfc86007', '2020-08-12 16:16:44.916+02', '2020-08-12 16:16:44.916+02', 'Author', 'author', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('bf800912-56c4-44eb-b425-64e51a9824fe', '2020-08-12 16:17:14.789+02', '2020-08-12 16:17:14.789+02', 'Senior Editor', 'seniorEditor', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+INSERT INTO public.teams (id, created, updated, name, role, members, owners, global, type, manuscript_id) VALUES ('0719d132-bb6c-49d9-9122-7911a48cfd60', '2020-08-12 16:17:43.258+02', '2020-08-13 15:09:41.898+02', 'Reviewers', 'reviewer', NULL, NULL, NULL, 'team', '93735475-08f6-436b-9db3-fe2364b9dbb2');
+
+
+--
+-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: test
+--
+
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('231717dd-ba09-43d4-ac98-9d5542b27a0c', '2020-07-22 14:18:36.597+02', '2020-07-24 16:43:54.939+02', NULL, NULL, '000000032536230X', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser5.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('027afa6a-edbc-486e-bb31-71e12f8ea1c5', '2020-07-21 16:17:24.734+02', '2020-08-12 16:17:13.598+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser2.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('3802b0e7-aadc-45de-9cf9-918fede99b97', '2020-07-21 16:30:45.719+02', '2020-08-12 16:17:14.868+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser6.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('40e3d054-9ac8-4c0f-84ed-e3c6307662cd', '2020-07-21 16:36:24.973+02', '2020-08-13 15:09:35.913+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser4.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-08-13 15:09:41.198+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser7.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('85e1300e-003c-4e96-987b-23812f902477', '2020-07-21 16:35:38.381+02', '2020-08-13 15:09:46.741+02', NULL, NULL, '0000000294294446', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser1.jpg', false);
+INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, teams, password_reset_token, password_reset_timestamp, type, profile_picture, online) VALUES ('1d599f2c-d293-4d5e-b6c1-ba34e81e3fc8', '2020-07-24 15:21:54.59+02', '2020-08-13 15:09:47.167+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', '/static/profiles/testuser3.jpg', true);
+
+
+--
+-- Name: job job_pkey; Type: CONSTRAINT; Schema: pgboss; Owner: test
+--
+
+ALTER TABLE ONLY pgboss.job
+    ADD CONSTRAINT job_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: version version_pkey; Type: CONSTRAINT; Schema: pgboss; Owner: test
+--
+
+ALTER TABLE ONLY pgboss.version
+    ADD CONSTRAINT version_pkey PRIMARY KEY (version);
+
+
+--
+-- Name: aliases aliases_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.aliases
+    ADD CONSTRAINT aliases_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: channel_members channel_members_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.channel_members
+    ADD CONSTRAINT channel_members_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: channels channels_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.channels
+    ADD CONSTRAINT channels_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: entities entities_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.entities
+    ADD CONSTRAINT entities_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: files files_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: identities identities_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.identities
+    ADD CONSTRAINT identities_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: manuscripts manuscripts_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.manuscripts
+    ADD CONSTRAINT manuscripts_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: messages messages_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.messages
+    ADD CONSTRAINT messages_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.migrations
+    ADD CONSTRAINT migrations_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: review_comments review_comments_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: reviews reviews_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.reviews
+    ADD CONSTRAINT reviews_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: team_members team_members_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.team_members
+    ADD CONSTRAINT team_members_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: teams teams_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.teams
+    ADD CONSTRAINT teams_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: users users_email_key; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.users
+    ADD CONSTRAINT users_email_key UNIQUE (email);
+
+
+--
+-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.users
+    ADD CONSTRAINT users_pkey PRIMARY KEY (id);
+
+
+--
+-- Name: users users_username_key; Type: CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.users
+    ADD CONSTRAINT users_username_key UNIQUE (username);
+
+
+--
+-- Name: archive_archivedon_idx; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE INDEX archive_archivedon_idx ON pgboss.archive USING btree (archivedon);
+
+
+--
+-- Name: archive_id_idx; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE INDEX archive_id_idx ON pgboss.archive USING btree (id);
+
+
+--
+-- Name: job_name; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE INDEX job_name ON pgboss.job USING btree (name text_pattern_ops);
+
+
+--
+-- Name: job_singletonkey; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE UNIQUE INDEX job_singletonkey ON pgboss.job USING btree (name, singletonkey) WHERE ((state < 'completed'::pgboss.job_state) AND (singletonon IS NULL));
+
+
+--
+-- Name: job_singletonkeyon; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE UNIQUE INDEX job_singletonkeyon ON pgboss.job USING btree (name, singletonon, singletonkey) WHERE (state < 'expired'::pgboss.job_state);
+
+
+--
+-- Name: job_singletonon; Type: INDEX; Schema: pgboss; Owner: test
+--
+
+CREATE UNIQUE INDEX job_singletonon ON pgboss.job USING btree (name, singletonon) WHERE ((state < 'expired'::pgboss.job_state) AND (singletonkey IS NULL));
+
+
+--
+-- Name: channel_members_idx; Type: INDEX; Schema: public; Owner: test
+--
+
+CREATE INDEX channel_members_idx ON public.channel_members USING btree (user_id, channel_id);
+
+
+--
+-- Name: is_default_idx; Type: INDEX; Schema: public; Owner: test
+--
+
+CREATE UNIQUE INDEX is_default_idx ON public.identities USING btree (is_default, user_id) WHERE (is_default IS TRUE);
+
+
+--
+-- Name: team_members_team_id_user_id_idx; Type: INDEX; Schema: public; Owner: test
+--
+
+CREATE INDEX team_members_team_id_user_id_idx ON public.team_members USING btree (team_id, user_id);
+
+
+--
+-- Name: teams_manuscript_id_idx; Type: INDEX; Schema: public; Owner: test
+--
+
+CREATE INDEX teams_manuscript_id_idx ON public.teams USING btree (manuscript_id);
+
+
+--
+-- Name: channel_members channel_members_channel_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.channel_members
+    ADD CONSTRAINT channel_members_channel_id_fkey FOREIGN KEY (channel_id) REFERENCES public.channels(id);
+
+
+--
+-- Name: channel_members channel_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.channel_members
+    ADD CONSTRAINT channel_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: channels channels_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.channels
+    ADD CONSTRAINT channels_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- Name: files files_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- Name: files files_review_comment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.files
+    ADD CONSTRAINT files_review_comment_id_fkey FOREIGN KEY (review_comment_id) REFERENCES public.review_comments(id) ON DELETE CASCADE;
+
+
+--
+-- Name: manuscripts manuscripts_submitter_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.manuscripts
+    ADD CONSTRAINT manuscripts_submitter_id_fkey FOREIGN KEY (submitter_id) REFERENCES public.users(id);
+
+
+--
+-- Name: messages messages_channel_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.messages
+    ADD CONSTRAINT messages_channel_id_fkey FOREIGN KEY (channel_id) REFERENCES public.channels(id) ON DELETE CASCADE;
+
+
+--
+-- Name: messages messages_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.messages
+    ADD CONSTRAINT messages_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: review_comments review_comments_review_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_review_id_fkey FOREIGN KEY (review_id) REFERENCES public.reviews(id) ON DELETE CASCADE;
+
+
+--
+-- Name: review_comments review_comments_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.review_comments
+    ADD CONSTRAINT review_comments_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE SET NULL;
+
+
+--
+-- Name: reviews reviews_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.reviews
+    ADD CONSTRAINT reviews_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- Name: identities sidentities_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.identities
+    ADD CONSTRAINT sidentities_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: team_members team_members_alias_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.team_members
+    ADD CONSTRAINT team_members_alias_id_fkey FOREIGN KEY (alias_id) REFERENCES public.aliases(id);
+
+
+--
+-- Name: team_members team_members_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.team_members
+    ADD CONSTRAINT team_members_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+
+--
+-- Name: team_members team_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.team_members
+    ADD CONSTRAINT team_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
+
+
+--
+-- Name: teams teams_manuscript_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: test
+--
+
+ALTER TABLE ONLY public.teams
+    ADD CONSTRAINT teams_manuscript_id_fkey FOREIGN KEY (manuscript_id) REFERENCES public.manuscripts(id) ON DELETE CASCADE;
+
+
+--
+-- PostgreSQL database dump complete
+--
+
diff --git a/cypress/integration/assign_reviewers_spec.js b/cypress/integration/assign_reviewers_spec.js
index 8b041451ce0f0697ea9e173f14dbf01859c7326f..84e8c7894ec7bc7b495eadbee17439fcbdedb0c2 100644
--- a/cypress/integration/assign_reviewers_spec.js
+++ b/cypress/integration/assign_reviewers_spec.js
@@ -1,9 +1,9 @@
-const login = name => {
-  cy.task('createToken', name).then(token => {
-    cy.setToken(token)
-    cy.visit('/journal/dashboard')
-  })
-}
+// const login = name => {
+//   cy.task('createToken', name).then(token => {
+//     cy.setToken(token)
+//     cy.visit('/journal/dashboard')
+//   })
+// }
 
 const inviteReviewer = name => {
   cy.get('*[aria-label*="Invite reviewers"]').click({
@@ -21,7 +21,7 @@ describe('Editor assigning reviewers', () => {
   it('can assign 3 reviewers', () => {
     cy.task('restore', 'senior_editor_assigned')
 
-    login('Joanne Pilger')
+    cy.login('Joanne Pilger')
 
     cy.contains('Control Panel').click()
     cy.contains('Manage Reviewers').click()
diff --git a/cypress/integration/decision_spec.js b/cypress/integration/decision_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..018bfae8ba5e71956c91fe6b04bcc058982858c1
--- /dev/null
+++ b/cypress/integration/decision_spec.js
@@ -0,0 +1,29 @@
+describe('Completing a review', () => {
+  it('accept and do a review', () => {
+    cy.task('restore', 'three_reviews_completed')
+
+    cy.login('Joanne Pilger') // Senior editor
+
+    cy.contains('Control Panel').click()
+
+    cy.get('[data-testid="decisionComment"]').click()
+    cy.focused().blur()
+
+    // Validations run on blur
+    cy.contains('Decision letter is required')
+    cy.get('[data-testid="decisionComment"] div[contenteditable="true"]')
+      .click({ force: true })
+      .type(`Great paper, dear authors, congratulations!`)
+
+    cy.contains('Accept').click({ force: true })
+    cy.contains('Submit').click()
+    cy.contains('Your decision has been saved.')
+    cy.visit('/journal/dashboard')
+    cy.contains('Accepted')
+
+    // Regression test, previously this count increased when a decision was made
+    cy.get('[data-testid="completed"]').should('have.text', '3completed')
+
+    cy.task('dump', 'decision_completed')
+  })
+})
diff --git a/cypress/integration/review_spec.js b/cypress/integration/review_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..a4a90e1bdc5e462f7f4a6554d63f506f0d178f80
--- /dev/null
+++ b/cypress/integration/review_spec.js
@@ -0,0 +1,38 @@
+const doReview = name => {
+  cy.login(name)
+
+  cy.get('[data-testid="accept-review"]').click()
+  cy.contains('Do Review').click()
+
+  cy.contains('My URL submission')
+  cy.contains('This is my data and code availability statement')
+
+  cy.get('[data-testid="reviewComment"]')
+    .click()
+    .type(`Great paper, congratulations! ${name}`)
+  cy.get('[data-testid="confidentialComment"]')
+    .click()
+    .type(`This is a very important paper. ${name}`)
+
+  cy.contains('Accept').click()
+  cy.contains('Submit').click()
+
+  cy.visit('/journal/dashboard')
+  cy.contains('Completed')
+}
+
+describe('Completing a review', () => {
+  it('accept and do a review', () => {
+    cy.task('restore', 'reviewers_invited')
+
+    doReview('Gale Davis') // Reviewers
+    doReview('Sherry Crofoot')
+    doReview('Elaine Barnes')
+
+    cy.login('Joanne Pilger') // Senior editor
+
+    cy.get('[data-testid="completed"]').should('have.text', '3completed')
+
+    cy.task('dump', 'three_reviews_completed')
+  })
+})
diff --git a/cypress/integration/submission_spec.js b/cypress/integration/submission_spec.js
index dbd1bd2df6ceb52b2fef2c775d2ddce555a773eb..6e98b5780ae19d36a1e7dc25051e8994f995c7ac 100644
--- a/cypress/integration/submission_spec.js
+++ b/cypress/integration/submission_spec.js
@@ -62,6 +62,20 @@ describe('URL submission test', () => {
         'https://doi.org/10.6084/m9.figshare.913521.v1, https://github.com/jure/mathtype_to_mathml',
       )
 
+    // Supplementary file upload
+    cy.fixture('test-pdf.pdf', 'base64').then(fileContent => {
+      cy.get('[data-testid="dropzone"]').attachFile(
+        'test-pdf.pdf',
+        // {
+        //   fileContent,
+        //   fileName: 'test-pdf.pdf',
+        //   encoding: 'base64',
+        //   mimeType: 'application/pdf',
+        // },
+        { subjectType: 'drag-n-drop' },
+      )
+    })
+
     cy.get('[data-testid="submission.keywords"]')
       .click()
       .type('some, keywords')
@@ -144,7 +158,7 @@ describe('URL submission test', () => {
 
     cy.contains('Control Panel').click()
     cy.contains('This is my data and code availability statement')
-
+    cy.contains('test-pdf.pdf')
     cy.task('dump', 'senior_editor_assigned')
   })
 })
diff --git a/cypress/screenshots/assign_reviewers_spec.js/Editor assigning reviewers -- can assign 3 reviewers (failed).png b/cypress/screenshots/assign_reviewers_spec.js/Editor assigning reviewers -- can assign 3 reviewers (failed).png
new file mode 100644
index 0000000000000000000000000000000000000000..9b353afdeae50e1fb53a41e80f817c0bb682414a
Binary files /dev/null and b/cypress/screenshots/assign_reviewers_spec.js/Editor assigning reviewers -- can assign 3 reviewers (failed).png differ
diff --git a/cypress/screenshots/review_spec.js/Editor assigning reviewers -- can assign 3 reviewers (failed).png b/cypress/screenshots/review_spec.js/Editor assigning reviewers -- can assign 3 reviewers (failed).png
new file mode 100644
index 0000000000000000000000000000000000000000..9b353afdeae50e1fb53a41e80f817c0bb682414a
Binary files /dev/null and b/cypress/screenshots/review_spec.js/Editor assigning reviewers -- can assign 3 reviewers (failed).png differ
diff --git a/cypress/screenshots/submission_spec.js/URL submission test -- can submit a URL and some metadata (failed).png b/cypress/screenshots/submission_spec.js/URL submission test -- can submit a URL and some metadata (failed).png
new file mode 100644
index 0000000000000000000000000000000000000000..c8aa71791004b322002139c08c144aeede81dae5
Binary files /dev/null and b/cypress/screenshots/submission_spec.js/URL submission test -- can submit a URL and some metadata (failed).png differ
diff --git a/cypress/screenshots/submission_spec.js/URL submission test -- senior editor can view the submission (failed).png b/cypress/screenshots/submission_spec.js/URL submission test -- senior editor can view the submission (failed).png
new file mode 100644
index 0000000000000000000000000000000000000000..30f7279081d5979ed210e72d724338a351c71aea
Binary files /dev/null and b/cypress/screenshots/submission_spec.js/URL submission test -- senior editor can view the submission (failed).png differ
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
index 9a0fa26c03700524169b7e4b94de90754412054c..348a2bcb967291476f69008018f2d6a80e0240fa 100644
--- a/cypress/support/commands.js
+++ b/cypress/support/commands.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-undef */
 import 'cypress-file-upload'
 
 // ***********************************************
@@ -27,6 +28,12 @@ import 'cypress-file-upload'
 // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
 
 Cypress.Commands.add('setToken', token => {
-  console.log('Setting token', token)
   localStorage.setItem('token', token)
 })
+
+Cypress.Commands.add('login', name => {
+  cy.task('createToken', name).then(token => {
+    cy.setToken(token)
+    cy.visit('/journal/dashboard')
+  })
+})
diff --git a/cypress/videos/assign_reviewers_spec.js.mp4 b/cypress/videos/assign_reviewers_spec.js.mp4
new file mode 100644
index 0000000000000000000000000000000000000000..865430db176851c5dd640d7fe4c8afea33a5f4fe
Binary files /dev/null and b/cypress/videos/assign_reviewers_spec.js.mp4 differ
diff --git a/cypress/videos/login_spec.js.mp4 b/cypress/videos/login_spec.js.mp4
index 677c7899add35cf4c405c59b7b5be934886774d4..9cef58dbb03a38ca8918a5b39eb7cd1f2572f926 100644
Binary files a/cypress/videos/login_spec.js.mp4 and b/cypress/videos/login_spec.js.mp4 differ
diff --git a/cypress/videos/submission_spec.js.mp4 b/cypress/videos/submission_spec.js.mp4
index c15776f8fd7e373407ac86e60ffcfe1fa103290d..3a89b44d3ff16445320d635b489efa0f5a2fd34f 100644
Binary files a/cypress/videos/submission_spec.js.mp4 and b/cypress/videos/submission_spec.js.mp4 differ
diff --git a/package.json b/package.json
index 1a16ea08843148ec905b6e15cb377152d8311c20..eb0d20005b982bfaef31646a82f534df1f31405b 100644
--- a/package.json
+++ b/package.json
@@ -143,8 +143,10 @@
     "start:services": "docker-compose up postgres",
     "start:server-and-client": "start-test server 'http://localhost:3000/healthcheck' client",
     "test:server-and-client": "start-test server:test 'http://localhost:3000/healthcheck' client",
-    "test:all": "start-test test:server-and-client 4000 test",
-    "test": "cypress run",
+    "test:all:firefox": "start-test test:server-and-client 4000 test:firefox",
+    "test:all:chrome": "start-test test:server-and-client 4000 test:chrome",
+    "test:firefox": "cypress run --browser firefox --headless",
+    "test:chrome": "cypress run --browser chrome",
     "__cleanNodeModules": "find . -name 'node_modules' -type d -prune -print -exec rm -rf '{}' \\;",
     "build": "NODE_ENV=production pubsweet build",
     "seedFromDump": "node scripts/seedFromDump.js"
diff --git a/server/component-xpub-review-backend/src/reviewBackend.js b/server/component-xpub-review-backend/src/reviewBackend.js
index bdee8d7b59b3f6fdd5a205cde4a0fcd44f2c967e..1132bc19e477e44581e24425e75415f30cdfee8b 100644
--- a/server/component-xpub-review-backend/src/reviewBackend.js
+++ b/server/component-xpub-review-backend/src/reviewBackend.js
@@ -82,8 +82,7 @@ module.exports = app => {
         {
           role: 'reviewer',
           name: 'Reviewer',
-          objectId: version.id,
-          objectType: 'fragment',
+          manuscriptId: version.id,
           members: [{ user: { id: reviewer[0].id } }],
         },
         { relate: true },
diff --git a/server/model-channel/src/migrations/1585323910-add-channels.sql b/server/model-channel/src/migrations/1585323910-add-channels.sql
index 00b50f44df9754f8af82b46adfe61a6c6cb9fc86..bde9bf147777365ba83232c162c7d28567161a7d 100644
--- a/server/model-channel/src/migrations/1585323910-add-channels.sql
+++ b/server/model-channel/src/migrations/1585323910-add-channels.sql
@@ -1,7 +1,6 @@
 CREATE TABLE channels (
   id UUID PRIMARY KEY,
   manuscript_id uuid REFERENCES manuscripts(id) ON DELETE CASCADE,
-  team_id uuid REFERENCES teams(id),
   created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
   updated TIMESTAMP WITH TIME ZONE,
   topic TEXT,
diff --git a/server/model-file/src/file.js b/server/model-file/src/file.js
index 64284826f7414791e1487bea8c05a5d25462abc3..de10f74af9ab6973e24f20e8ea98d661f3581e2f 100644
--- a/server/model-file/src/file.js
+++ b/server/model-file/src/file.js
@@ -1,5 +1,4 @@
 const BaseModel = require('@pubsweet/base-model')
-const logger = require('@pubsweet/logger')
 
 class File extends BaseModel {
   static get tableName() {
@@ -11,6 +10,29 @@ class File extends BaseModel {
     this.type = 'file'
   }
 
+  static get relationMappings() {
+    const { Manuscript, ReviewComment } = require('@pubsweet/models')
+
+    return {
+      manuscript: {
+        relation: BaseModel.BelongsToOneRelation,
+        modelClass: Manuscript,
+        join: {
+          from: 'manuscripts.id',
+          to: 'files.manuscript_id',
+        },
+      },
+      reviewComment: {
+        relation: BaseModel.BelongsToOneRelation,
+        modelClass: ReviewComment,
+        join: {
+          from: 'review_comments.id',
+          to: 'files.review_comments_id',
+        },
+      },
+    }
+  }
+
   static get schema() {
     return {
       properties: {
@@ -20,25 +42,11 @@ class File extends BaseModel {
         fileType: { type: ['string', 'null'] },
         filename: { type: ['string', 'null'] },
         size: { type: ['integer', 'null'] },
-        object: { type: ['string', 'null'] },
-        objectId: { type: 'string', format: 'uuid' },
+        reviewCommentId: { type: ['string', 'null'], format: 'uuid' },
+        manuscriptId: { type: ['string', 'null'], format: 'uuid' },
       },
     }
   }
-
-  static async findByObject({ object, object_id }) {
-    logger.debug('Finding Files by Object')
-
-    const results = await this.query()
-      .where('object', object)
-      .andWhere('object_id', object_id)
-
-    return results
-  }
-
-  // async $beforeDelete() {
-  //   await Team.deleteAssociated(this.data.type, this.id)
-  // }
 }
 
 File.type = 'file'
diff --git a/server/model-file/src/index.js b/server/model-file/src/index.js
index 5f1d108b1e4e4ec0e9e547db5bdbb08af74de6a6..3146f811a5304a8da93c489f57f81acad29efbd6 100644
--- a/server/model-file/src/index.js
+++ b/server/model-file/src/index.js
@@ -1,10 +1,8 @@
 const resolvers = require('./resolvers')
 const typeDefs = require('./typeDefs')
-const model = require('./file')
 
 module.exports = {
-  model,
-  modelName: 'File',
+  models: [{ modelName: 'File', model: require('./file') }],
   resolvers,
   typeDefs,
 }
diff --git a/server/model-file/src/migrations/1537450834-files.sql b/server/model-file/src/migrations/1537450834-files.sql
deleted file mode 100644
index c18083f7b9455aefcadbabe42d3821d1052f5d24..0000000000000000000000000000000000000000
--- a/server/model-file/src/migrations/1537450834-files.sql
+++ /dev/null
@@ -1,14 +0,0 @@
-CREATE TABLE files (
-    id UUID PRIMARY KEY,
-    created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
-    updated TIMESTAMP WITH TIME ZONE,
-    object TEXT, 
-    object_id UUID,
-    label TEXT,
-    file_type TEXT,
-    filename TEXT,
-    url TEXT,
-    mime_type TEXT,
-    size INTEGER,
-    type TEXT NOT NULL
-);
\ No newline at end of file
diff --git a/server/model-file/src/migrations/1596838897-files.sql b/server/model-file/src/migrations/1596838897-files.sql
new file mode 100644
index 0000000000000000000000000000000000000000..f823bc1ce840b4300e346776bf163aff24fdf889
--- /dev/null
+++ b/server/model-file/src/migrations/1596838897-files.sql
@@ -0,0 +1,24 @@
+CREATE TABLE files (
+    id UUID PRIMARY KEY,
+    created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
+    updated TIMESTAMP WITH TIME ZONE,
+    -- object_type TEXT,
+    -- object_id UUID,
+    label TEXT,
+    file_type TEXT,
+    filename TEXT,
+    url TEXT,
+    mime_type TEXT,
+    size INTEGER,
+    type TEXT NOT NULL,
+    -- Things that can have files
+    manuscript_id UUID REFERENCES manuscripts(id) ON DELETE CASCADE,
+    review_comment_id UUID REFERENCES review_comments(id) ON DELETE CASCADE,
+
+    constraint exactly_one_file_owner check(
+      (
+        (manuscript_id is not null)::integer +
+        (review_comment_id is not null)::integer
+      ) = 1
+    )
+);
\ No newline at end of file
diff --git a/server/model-file/src/resolvers.js b/server/model-file/src/resolvers.js
index da8ea91c7c9c0b3e9a1423ac232df849defd7a10..5001766ded287034c90740b1662935484791cf5f 100644
--- a/server/model-file/src/resolvers.js
+++ b/server/model-file/src/resolvers.js
@@ -10,8 +10,8 @@ const randomBytes = promisify(crypto.randomBytes)
 const uploadsPath = config.get('pubsweet-server').uploads
 
 const upload = async file => {
-  const { stream, filename, encoding } = await file
-
+  const { createReadStream, filename, encoding } = await file
+  const stream = createReadStream()
   const raw = await randomBytes(16)
   const generatedFilename = raw.toString('hex') + path.extname(filename)
   const outPath = path.join(uploadsPath, generatedFilename)
@@ -32,6 +32,7 @@ const resolvers = {
       const path = await upload(file)
       meta.url = `/static/${path}`
       const data = await new File(meta).save()
+
       return data
     },
   },
diff --git a/server/model-file/src/typeDefs.js b/server/model-file/src/typeDefs.js
index 34050c96a91107418bba4e9e5951163a8bc5dc72..d9d84614fc758b9025cb5b5423fbe8a5b0c85967 100644
--- a/server/model-file/src/typeDefs.js
+++ b/server/model-file/src/typeDefs.js
@@ -8,8 +8,8 @@ const typeDefs = `
     fileType: String
     filename: String
     mimeType: String
-    object: String
-    objectId: ID!
+    manuscriptId: ID
+    reviewCommentId: ID
     label: String
     size: Int
   }
@@ -18,8 +18,8 @@ const typeDefs = `
     id: ID!
     created: DateTime!
     updated: DateTime
-    object: String
-    objectId: ID!
+    manuscriptId: ID
+    reviewCommentId: ID
     label: String
     fileType: String
     filename: String
diff --git a/server/model-manuscript/src/graphql.js b/server/model-manuscript/src/graphql.js
index cc598e633a7b61e98189582678707f64524add96..c3819736ac1427bc73f031ed0e1e5b967dadfdb5 100644
--- a/server/model-manuscript/src/graphql.js
+++ b/server/model-manuscript/src/graphql.js
@@ -5,8 +5,6 @@ const { ref } = require('objection')
 const resolvers = {
   Mutation: {
     async createManuscript(_, vars, ctx) {
-      const { Team } = require('@pubsweet/models')
-
       const { meta, files } = vars.input
 
       // We want the submission information to be stored as JSONB
@@ -31,55 +29,38 @@ const resolvers = {
         status: 'new',
         submission,
         submitterId: ctx.user.id,
+        // Create two channels: 1. free for all involved, 2. editorial
+        channels: [
+          {
+            topic: 'Manuscript discussion',
+            type: 'all',
+          },
+          {
+            topic: 'Editorial discussion',
+            type: 'editorial',
+          },
+        ],
+        files: files.map(file =>
+          Object.assign({}, file, {
+            fileType: 'manuscript',
+          }),
+        ),
+        reviews: [],
+        teams: [
+          {
+            role: 'author',
+            name: 'Author',
+            members: [{ user: { id: ctx.user.id } }],
+          },
+        ],
       }
 
-      // eslint-disable-next-line
-      const manuscript = await new ctx.models.Manuscript(
+      const manuscript = await ctx.models.Manuscript.query().upsertGraphAndFetch(
         emptyManuscript,
-      ).saveGraph()
-
-      // Create two channels: 1. free for all involved, 2. editorial
-      const allChannel = new ctx.models.Channel({
-        manuscriptId: manuscript.id,
-        topic: 'Manuscript discussion',
-        type: 'all',
-      }).save()
-
-      const editorialChannel = new ctx.models.Channel({
-        manuscriptId: manuscript.id,
-        topic: 'Editorial discussion',
-        type: 'editorial',
-      }).save()
-
-      manuscript.manuscriptVersions = []
-      manuscript.files = []
-      files.map(async file => {
-        const newFile = Object.assign({}, file, {
-          fileType: 'manuscript',
-          object: 'Manuscript',
-          objectId: manuscript.id,
-        })
-        manuscript.files.push(
-          // eslint-disable-next-line
-          await new ctx.models.File(newFile).save(),
-        )
-      })
-
-      manuscript.reviews = []
-
-      const createdTeam = await Team.query().upsertGraphAndFetch(
-        {
-          role: 'author',
-          name: 'Author',
-          objectId: manuscript.id,
-          objectType: 'Manuscript',
-          members: [{ user: { id: ctx.user.id } }],
-        },
         { relate: true },
       )
 
-      manuscript.teams = [createdTeam]
-      manuscript.channels = [allChannel, editorialChannel]
+      manuscript.manuscriptVersions = []
       return manuscript
     },
     async deleteManuscript(_, { id }, ctx) {
@@ -134,7 +115,7 @@ const resolvers = {
           recommendation: '',
           isDecision: false,
           userId: currentUserId,
-          manuscriptId: team.objectId,
+          manuscriptId: team.manuscriptId,
         }
         await new Review(review).save()
       }
@@ -184,8 +165,7 @@ const resolvers = {
 
       // Create a new team of reviewers if it doesn't exist
       const newTeam = await new ctx.models.Team({
-        objectId: manuscriptId,
-        objectType: 'Manuscript',
+        manuscriptId,
         members: [{ status: 'invited', userId }],
         role: 'reviewer',
         name: 'Reviewers',
@@ -219,7 +199,7 @@ const resolvers = {
 
       const manuscript = await Manuscript.query()
         .findById(id)
-        .eager('[teams, channels, reviews]')
+        .eager('[teams, channels, reviews.[user, comments], files]')
 
       if (!manuscript.meta) {
         manuscript.meta = {}
@@ -235,10 +215,6 @@ const resolvers = {
         },
       ]
       manuscript.decision = ''
-      manuscript.files = await ctx.models.File.findByObject({
-        object: 'Manuscript',
-        object_id: manuscript.id,
-      })
 
       manuscript.manuscriptVersions = await manuscript.getManuscriptVersions()
       // manuscript.channel = await ctx.connectors.Channel.model.find(
diff --git a/server/model-manuscript/src/manuscript.js b/server/model-manuscript/src/manuscript.js
index 129205d139016f9cfd44cca933f491fa077d5956..16a9b26d8d3db838373293af8e353d6d4b1bd9c7 100644
--- a/server/model-manuscript/src/manuscript.js
+++ b/server/model-manuscript/src/manuscript.js
@@ -2,7 +2,6 @@ const BaseModel = require('@pubsweet/base-model')
 const omit = require('lodash/omit')
 const cloneDeep = require('lodash/cloneDeep')
 const sortBy = require('lodash/sortBy')
-const values = require('lodash/values')
 
 class Manuscript extends BaseModel {
   static get tableName() {
@@ -14,48 +13,48 @@ class Manuscript extends BaseModel {
     this.type = 'Manuscript'
   }
 
-  static async myManuscripts(myManuscripts) {
-    const mainManuscript = {}
-    myManuscripts.forEach(manuscript => {
-      if (!mainManuscript[manuscript.parentId || manuscript.id]) {
-        mainManuscript[manuscript.parentId || manuscript.id] = manuscript
-      } else {
-        const checkManuscript =
-          mainManuscript[manuscript.parentId || manuscript.id]
-        // Compare Dates
-        const dateCheckManuscript = new Date(checkManuscript.created).getTime()
-        const dateManuscript = new Date(manuscript.created).getTime()
-        if (dateManuscript >= dateCheckManuscript) {
-          mainManuscript[manuscript.parentId || manuscript.id] = manuscript
-        }
-      }
-    })
-
-    const latestManuscripts = values(mainManuscript)
-    await Promise.all(
-      latestManuscripts.map(async manuscript => {
-        manuscript.teams = await new Manuscript(manuscript).getTeams()
-        manuscript.reviews = await new Manuscript(manuscript).getReviews()
-        manuscript.manuscriptVersions =
-          (await manuscript.getManuscriptVersions()) || []
-        return manuscript
-      }),
-    )
-
-    return latestManuscripts
-  }
-
-  async getTeams() {
-    const { Team } = require('@pubsweet/models')
-    const myTeams = await Team.query()
-      .where({
-        objectId: this.id,
-        objectType: 'Manuscript',
-      })
-      .eager('members')
-
-    return myTeams
-  }
+  // static async myManuscripts(myManuscripts) {
+  //   const mainManuscript = {}
+  //   myManuscripts.forEach(manuscript => {
+  //     if (!mainManuscript[manuscript.parentId || manuscript.id]) {
+  //       mainManuscript[manuscript.parentId || manuscript.id] = manuscript
+  //     } else {
+  //       const checkManuscript =
+  //         mainManuscript[manuscript.parentId || manuscript.id]
+  //       // Compare Dates
+  //       const dateCheckManuscript = new Date(checkManuscript.created).getTime()
+  //       const dateManuscript = new Date(manuscript.created).getTime()
+  //       if (dateManuscript >= dateCheckManuscript) {
+  //         mainManuscript[manuscript.parentId || manuscript.id] = manuscript
+  //       }
+  //     }
+  //   })
+
+  //   const latestManuscripts = values(mainManuscript)
+  //   await Promise.all(
+  //     latestManuscripts.map(async manuscript => {
+  //       manuscript.teams = await new Manuscript(manuscript).getTeams()
+  //       manuscript.reviews = await new Manuscript(manuscript).getReviews()
+  //       manuscript.manuscriptVersions =
+  //         (await manuscript.getManuscriptVersions()) || []
+  //       return manuscript
+  //     }),
+  //   )
+
+  //   return latestManuscripts
+  // }
+
+  // async getTeams() {
+  //   const { Team } = require('@pubsweet/models')
+  //   const myTeams = await Team.query()
+  //     .where({
+  //       objectId: this.id,
+  //       objectType: 'Manuscript',
+  //     })
+  //     .eager('members')
+
+  //   return myTeams
+  // }
 
   async getReviews() {
     // TODO: Use relationships
@@ -73,15 +72,15 @@ class Manuscript extends BaseModel {
   }
 
   async getManuscriptVersions() {
-    const { File } = require('@pubsweet/models')
+    // const { File } = require('@pubsweet/models')
 
     const id = this.parentId || this.id
     const manuscripts = await Manuscript.query()
       .where('parent_id', id)
-      .eager('[teams, teams.members, reviews]')
+      .eager('[teams, teams.members, reviews, files]')
     const firstManuscript = await Manuscript.query()
       .findById(id)
-      .eager('[teams, teams.members, reviews]')
+      .eager('[teams, teams.members, reviews, files]')
     manuscripts.push(firstManuscript)
 
     const manuscriptVersionsArray = manuscripts.filter(
@@ -95,16 +94,6 @@ class Manuscript extends BaseModel {
       manuscript => new Date(manuscript.created),
     )
 
-    await Promise.all(
-      manuscriptVersions.map(async manuscript => {
-        manuscript.files = await File.findByObject({
-          object: 'Manuscript',
-          object_id: manuscript.id,
-        })
-        return manuscript
-      }),
-    )
-
     return manuscriptVersions
   }
 
@@ -122,9 +111,8 @@ class Manuscript extends BaseModel {
         team.role === 'handlingEditor',
     )
 
-    const manuscriptFiles = await File.findByObject({
-      object: 'Manuscript',
-      object_id: this.id,
+    const manuscriptFiles = await File.query().where({
+      manuscriptId: this.id,
     })
 
     const manuscriptDecision = manuscriptReviews.find(
@@ -147,7 +135,7 @@ class Manuscript extends BaseModel {
       // Copy Teams to the new Version
       await Promise.all(
         teams.map(async team => {
-          team.objectId = newManuscript.id
+          team.manuscriptId = newManuscript.id
           team.members = team.members.map(member => omit(member, 'id'))
           await new Team(omit(team, ['id'])).saveGraph()
         }),
@@ -158,7 +146,7 @@ class Manuscript extends BaseModel {
     await Promise.all(
       manuscriptFiles.map(async file => {
         const newFile = omit(file, ['id'])
-        newFile.objectId = newManuscript.id
+        newFile.manuscriptId = newManuscript.id
         await new File(newFile).save()
         return newFile
       }),
@@ -168,7 +156,7 @@ class Manuscript extends BaseModel {
   }
 
   static get relationMappings() {
-    const { Channel, User, Team, Review } = require('@pubsweet/models')
+    const { Channel, User, Team, Review, File } = require('@pubsweet/models')
 
     return {
       submitter: {
@@ -190,13 +178,17 @@ class Manuscript extends BaseModel {
       teams: {
         relation: BaseModel.HasManyRelation,
         modelClass: Team,
-        beforeInsert(model) {
-          model.objectType = 'Manuscript'
+        join: {
+          from: 'manuscripts.id',
+          to: 'teams.manuscriptId',
         },
-        filter: { objectType: 'Manuscript' },
+      },
+      files: {
+        relation: BaseModel.HasManyRelation,
+        modelClass: File,
         join: {
           from: 'manuscripts.id',
-          to: 'teams.objectId',
+          to: 'files.manuscriptId',
         },
       },
       reviews: {
@@ -300,35 +292,6 @@ class Manuscript extends BaseModel {
       },
     }
   }
-
-  // TODO: Do this on the DB level with cascading deletes
-  async $beforeDelete() {
-    // const Review = require('../../review/src/review')
-    const { Review, Team, File } = require('@pubsweet/models')
-
-    const files = await File.findByObject({
-      object_id: this.id,
-      object: 'Manuscript',
-    })
-    if (files.length > 0) {
-      files.forEach(async fl => {
-        await new File(fl).delete()
-      })
-    }
-
-    const review = await Review.findByField('manuscript_id', this.id)
-    if (review.length > 0) {
-      review.forEach(async rv => {
-        await new Review(rv).delete()
-      })
-    }
-
-    this.teams = await this.getTeams()
-
-    this.teams.forEach(async team => {
-      await new Team(team).delete()
-    })
-  }
 }
 
 Manuscript.type = 'Manuscript'
diff --git a/server/model-review/src/index.js b/server/model-review/src/index.js
index 3d9d6f4c8bec12c8e7c9fb45ac05a217c7c5f517..92e1efbc087a696c0c6d9eb3cd9fe23701e00ff7 100644
--- a/server/model-review/src/index.js
+++ b/server/model-review/src/index.js
@@ -1,10 +1,11 @@
 const resolvers = require('./resolvers')
 const typeDefs = require('./typeDefs')
-const model = require('./review')
 
 module.exports = {
-  model,
-  modelName: 'Review',
   resolvers,
   typeDefs,
+  models: [
+    { modelName: 'Review', model: require('./review') },
+    { modelName: 'ReviewComment', model: require('./review_comment') },
+  ],
 }
diff --git a/server/model-review/src/migrations/1537450834-review.sql b/server/model-review/src/migrations/1596830547-review.sql
similarity index 79%
rename from server/model-review/src/migrations/1537450834-review.sql
rename to server/model-review/src/migrations/1596830547-review.sql
index d679395061b63365a604beb417e7560e1194c52a..56abf5f5a6cf2b904cb12bc6cd92c04be0813f1a 100644
--- a/server/model-review/src/migrations/1537450834-review.sql
+++ b/server/model-review/src/migrations/1596830547-review.sql
@@ -4,8 +4,7 @@ CREATE TABLE reviews (
     updated TIMESTAMP WITH TIME ZONE,
     recommendation TEXT,
     is_decision BOOLEAN DEFAULT FALSE,
-    comments JSONB,
     user_id UUID,
-    manuscript_id UUID,
+    manuscript_id UUID REFERENCES manuscripts(id) ON DELETE CASCADE,
     type TEXT NOT NULL
 );
\ No newline at end of file
diff --git a/server/model-review/src/migrations/1596830548-add-review-comments.sql b/server/model-review/src/migrations/1596830548-add-review-comments.sql
new file mode 100644
index 0000000000000000000000000000000000000000..7a7843c91eec2b04d7162506049afa87c7874034
--- /dev/null
+++ b/server/model-review/src/migrations/1596830548-add-review-comments.sql
@@ -0,0 +1,12 @@
+CREATE TABLE review_comments (
+    id UUID PRIMARY KEY,
+    created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
+    updated TIMESTAMP WITH TIME ZONE,
+    -- If a review is deleted for some reason, we want its comments to go away too
+    review_id UUID REFERENCES reviews(id) ON DELETE CASCADE,
+    -- If a user is deleted, it's likely we want to keep the comments they have made
+    user_id UUID REFERENCES users(id) ON DELETE SET NULL,
+    content TEXT,
+    comment_type TEXT,
+    type TEXT
+);
\ No newline at end of file
diff --git a/server/model-review/src/resolvers.js b/server/model-review/src/resolvers.js
index 9a8fc508e4d9f8d9e78e90e6e26bc7e8a9d8882a..566b16eb3abcee690d095bdb239c07bb5bcceaa0 100644
--- a/server/model-review/src/resolvers.js
+++ b/server/model-review/src/resolvers.js
@@ -1,25 +1,33 @@
-const merge = require('lodash/merge')
-const Review = require('./review')
+// const merge = require('lodash/merge')
+// const Review = require('./review')
 
 const resolvers = {
   Mutation: {
     async updateReview(_, { id, input }, ctx) {
-      if (id) {
-        const review = await ctx.models.Review.query().findById(id)
-        const update = merge({}, review, input)
-        // Load Review
-        const rvw = await ctx.models.Review.query().updateAndFetchById(
-          id,
-          update,
-        )
-        rvw.comments = await rvw.getComments()
+      // We process comment fields into array
+      input.user = ctx.user
+      const processedReview = Object.assign({}, input)
+      processedReview.comments = [
+        input.reviewComment,
+        input.confidentialComment,
+        input.decisionComment,
+      ].filter(Boolean)
+
+      delete processedReview.reviewComment
+      delete processedReview.confidentialComment
+      delete processedReview.decisionComment
 
-        return rvw
-      }
-      input.userId = ctx.user.id
-      const review = await new Review(input)
-      await review.save()
-      review.comments = await review.getComments()
+      const review = await ctx.models.Review.query().upsertGraphAndFetch(
+        {
+          id,
+          ...processedReview,
+        },
+        {
+          relate: true,
+          noUnrelate: true,
+          noDelete: true,
+        },
+      )
 
       return review
     },
@@ -43,6 +51,13 @@ const resolvers = {
       return member.save()
     },
   },
+  ReviewComment: {
+    async files(parent, _, ctx) {
+      return parent.files
+        ? parent.files
+        : ctx.models.File.query().where({ reviewCommentId: parent.id })
+    },
+  },
 }
 
 module.exports = resolvers
diff --git a/server/model-review/src/review.js b/server/model-review/src/review.js
index bc07009f5ad9c90e1c7a4491216188be8714a93f..5de35471ada48191e7caba5b4151844b532b8136 100644
--- a/server/model-review/src/review.js
+++ b/server/model-review/src/review.js
@@ -10,30 +10,33 @@ class Review extends BaseModel {
     this.type = 'Review'
   }
 
-  async user() {
-    const { User } = require('@pubsweet/models')
-    return User.find(this.userId)
-  }
+  // async user() {
+  //   const { User } = require('@pubsweet/models')
+  //   return User.find(this.userId)
+  // }
 
-  async getComments() {
-    const File = require('../../model-file/src/file')
+  // async getComments() {
+  //   const File = require('../../model-file/src/file')
 
-    await Promise.all(
-      (this.comments || []).map(async comment => {
-        const files = await File.findByObject({
-          object: 'Review',
-          object_id: this.id,
-        })
-        const commentFile = files.find(file => file.fileType === comment.type)
-        if (commentFile) {
-          comment.files = [commentFile]
-        }
-        return comment
-      }),
-    )
+  //   await Promise.all(
+  //     (this.comments || []).map(async comment => {
+  //       const files = await File.query().where({
+  //         objectType: 'Review',
+  //         objectId: this.id,
+  //       })
+  //       const commentFile = files.find(file => file.fileType === comment.type)
+  //       if (commentFile) {
+  //         comment.files = [commentFile]
+  //       }
+  //       return comment
+  //     }),
+  //   )
 
-    this.user = this.user()
-    return this.comments
+  //   this.user = this.user()
+  //   return this.comments
+  // }
+  static get relatedFindQueryMutates() {
+    return false
   }
 
   static get schema() {
@@ -44,15 +47,12 @@ class Review extends BaseModel {
         userId: { type: 'string', format: 'uuid' },
         user: { type: ['object', 'null'] },
         isDecision: { type: ['boolean', 'false'] },
-        comments: {
-          type: ['array', 'null'],
-        },
       },
     }
   }
 
   static get relationMappings() {
-    const { Manuscript, User } = require('@pubsweet/models')
+    const { Manuscript, User, ReviewComment } = require('@pubsweet/models')
 
     return {
       manuscript: {
@@ -71,21 +71,46 @@ class Review extends BaseModel {
           to: 'users.id',
         },
       },
+      comments: {
+        relation: BaseModel.HasManyRelation,
+        modelClass: ReviewComment,
+        join: {
+          from: 'review_comments.reviewId',
+          to: 'reviews.id',
+        },
+      },
     }
   }
 
-  async $beforeDelete() {
-    const File = require('../../model-file/src/file')
-    const files = await File.findByObject({
-      object_id: this.id,
-      object: 'Review',
-    })
-    if (files.length > 0) {
-      files.forEach(async fl => {
-        await new File(fl).delete()
-      })
+  async $afterGet() {
+    if (this.isDecision) {
+      this.decisionComment = await this.$relatedQuery('comments')
+        .where('commentType', 'decision')
+        .first()
+    } else {
+      this.reviewComment = await this.$relatedQuery('comments')
+        .where('commentType', 'review')
+        .first()
+
+      this.confidentialComment = await this.$relatedQuery('comments')
+        .where('commentType', 'confidential')
+        .first()
     }
+    return true
   }
+
+  // async $beforeDelete() {
+  //   const File = require('../../model-file/src/file')
+  //   const files = await File.query().where({
+  //     objectId: this.id,
+  //     objectType: 'Review',
+  //   })
+  //   if (files.length > 0) {
+  //     files.forEach(async fl => {
+  //       await new File(fl).delete()
+  //     })
+  //   }
+  // }
 }
 
 Review.type = 'Review'
diff --git a/server/model-review/src/review_comment.js b/server/model-review/src/review_comment.js
new file mode 100644
index 0000000000000000000000000000000000000000..6ba36c8a7eb44cc5c22298a3ababeff8b0dc594f
--- /dev/null
+++ b/server/model-review/src/review_comment.js
@@ -0,0 +1,57 @@
+const BaseModel = require('@pubsweet/base-model')
+
+class ReviewComment extends BaseModel {
+  static get tableName() {
+    return 'review_comments'
+  }
+
+  constructor(properties) {
+    super(properties)
+    this.type = 'ReviewComment'
+  }
+
+  static get schema() {
+    return {
+      properties: {
+        content: { type: ['string', 'null'] },
+        userId: { type: 'string', format: 'uuid' },
+        reviewId: { type: 'string', format: 'uuid' },
+        commentType: { type: ['string', 'null'] },
+      },
+    }
+  }
+
+  static get relationMappings() {
+    const { File, Review, User } = require('@pubsweet/models')
+
+    return {
+      review: {
+        relation: BaseModel.BelongsToOneRelation,
+        modelClass: Review,
+        join: {
+          from: 'review_comments.reviewId',
+          to: 'reviews.id',
+        },
+      },
+      user: {
+        relation: BaseModel.BelongsToOneRelation,
+        modelClass: User,
+        join: {
+          from: 'reviews_comments.userId',
+          to: 'users.id',
+        },
+      },
+      files: {
+        relation: BaseModel.HasManyRelation,
+        modelClass: File,
+        join: {
+          from: 'review_comments.id',
+          to: 'files.review_comment_id',
+        },
+      },
+    }
+  }
+}
+
+ReviewComment.type = 'Review'
+module.exports = ReviewComment
diff --git a/server/model-review/src/typeDefs.js b/server/model-review/src/typeDefs.js
index fb39b5e3903bb16633a2860330a201fa9a683165..43b7dc62d4336c86761581bfad24dc4739061f1f 100644
--- a/server/model-review/src/typeDefs.js
+++ b/server/model-review/src/typeDefs.js
@@ -8,29 +8,37 @@ const typeDefs = `
     id: ID!
     created: DateTime!
     updated: DateTime
-    comments: [Comment]
     recommendation: String
     isDecision: Boolean
     open: Boolean
     user: User
+    reviewComment: ReviewComment
+    confidentialComment: ReviewComment
+    decisionComment: ReviewComment
   }
 
   input ReviewInput {
-    comments: [CommentInput]
+    reviewComment: ReviewCommentInput
+    confidentialComment: ReviewCommentInput
+    decisionComment: ReviewCommentInput
     recommendation: String
     isDecision: Boolean
     manuscriptId: ID!
   }
 
-  input CommentInput {
-    type: String
+  type ReviewComment implements Object {
+    id: ID!
+    created: DateTime!
+    updated: DateTime
+    commentType: String
     content: String
+    files: [File]
   }
 
-  type Comment {
-    type: String
+  input ReviewCommentInput {
+    id: ID
+    commentType: String
     content: String
-    files: [File]
   }
 `
 
diff --git a/server/model-team/src/graphql.js b/server/model-team/src/graphql.js
index 2e6cac60789d90544225f6e6672349d32f6e49cf..9ea0cf9fcf0989d897e43e92749dbc563db1d9f2 100644
--- a/server/model-team/src/graphql.js
+++ b/server/model-team/src/graphql.js
@@ -62,9 +62,8 @@ const resolvers = {
       const t = await ctx.models.Team.query().findById(team.id)
       return t.$relatedQuery('members')
     },
-    object(team, vars, ctx) {
-      const { objectId, objectType } = team
-      return objectId && objectType ? { objectId, objectType } : null
+    manuscript(parent, vars, ctx) {
+      return ctx.models.Manuscript.query().findById(parent.manuscriptId)
     },
   },
   TeamMember: {
@@ -100,7 +99,7 @@ const typeDefs = `
     type: String!
     role: String!
     name: String
-    object: TeamObject
+    manuscript: Manuscript
     members: [TeamMember!]
     owners: [User]
     global: Boolean
@@ -136,16 +135,10 @@ const typeDefs = `
     aff: String
   }
 
-  type TeamObject {
-    objectId: ID!
-    objectType: String!
-  }
-
   input TeamInput {
     role: String
     name: String
-    objectId: ID
-    objectType: String
+    manuscriptId: ID
     members: [TeamMemberInput]
     global: Boolean
   }
@@ -153,8 +146,7 @@ const typeDefs = `
   input TeamWhereInput {
     role: String
     name: String
-    objectId: ID
-    objectType: String
+    manuscriptId: ID
     members: [TeamMemberInput]
     global: Boolean
     users: [ID!]
diff --git a/server/model-team/src/migrations/1542801241-initial-team-migration.sql b/server/model-team/src/migrations/1542801241-initial-team-migration.sql
deleted file mode 100644
index 0efd6b9fce08a1b77f9ed2bffbdd9fb8a70a5992..0000000000000000000000000000000000000000
--- a/server/model-team/src/migrations/1542801241-initial-team-migration.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-CREATE TABLE teams (
-  id UUID PRIMARY KEY,
-  created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
-  updated TIMESTAMP WITH TIME ZONE,
-  object JSONB,
-  name TEXT,
-  role TEXT NOT NULL,
-  members JSONB,
-  owners JSONB,
-  global BOOLEAN,
-  type TEXT NOT NULL
-);
\ No newline at end of file
diff --git a/server/model-team/src/migrations/1547596236-initial-team-member-migration.js b/server/model-team/src/migrations/1547596236-initial-team-member-migration.js
deleted file mode 100644
index 143299d1798836425f1ed6c3c171ecb29833ccf2..0000000000000000000000000000000000000000
--- a/server/model-team/src/migrations/1547596236-initial-team-member-migration.js
+++ /dev/null
@@ -1,21 +0,0 @@
-exports.up = knex =>
-  knex.schema.createTable('team_members', table => {
-    table.uuid('id').primary()
-    table.timestamp('created').defaultTo(knex.fn.now())
-    table.timestamp('updated').defaultTo(knex.fn.now())
-    table.string('status')
-    table
-      .uuid('team_id')
-      .references('id')
-      .inTable('teams')
-      .onDelete('CASCADE')
-      .onUpdate('CASCADE')
-    table
-      .uuid('user_id')
-      .references('id')
-      .inTable('users')
-      .onDelete('CASCADE')
-      .onUpdate('CASCADE')
-
-    table.index(['team_id', 'user_id'])
-  })
diff --git a/server/model-team/src/migrations/1548205275-move-members.js b/server/model-team/src/migrations/1548205275-move-members.js
deleted file mode 100644
index fa8763a58d9012ff67abfd8b83eb9e26f7deabf3..0000000000000000000000000000000000000000
--- a/server/model-team/src/migrations/1548205275-move-members.js
+++ /dev/null
@@ -1,20 +0,0 @@
-exports.up = async knex => {
-  const { Team, TeamMember } = require('@pubsweet/models')
-  const teams = await Team.query()
-
-  const saves = []
-
-  teams.forEach(team => {
-    if (team.members) {
-      team.members.forEach(member => {
-        saves.push(new TeamMember({ userId: member, teamId: team.id }).save())
-      })
-    }
-  })
-
-  await Promise.all(saves)
-
-  return knex.schema.table('teams', table => {
-    table.dropColumn('members')
-  })
-}
diff --git a/server/model-team/src/migrations/1548205276-simplify-object.js b/server/model-team/src/migrations/1548205276-simplify-object.js
deleted file mode 100644
index f04443c6a26bf553ff9aa5bc01ed950ae89a2386..0000000000000000000000000000000000000000
--- a/server/model-team/src/migrations/1548205276-simplify-object.js
+++ /dev/null
@@ -1,27 +0,0 @@
-exports.up = async knex => {
-  const { Team } = require('@pubsweet/models')
-  const teams = await Team.query()
-
-  const saves = []
-
-  await knex.schema.table('teams', table => {
-    table.uuid('object_id')
-    table.string('object_type')
-    table.index(['object_id', 'object_type'])
-  })
-
-  teams.forEach(team => {
-    if (team.object && team.object.objectId && team.object.objectType) {
-      team.objectId = team.object.objectId
-      team.objectType = team.object.objectType
-      delete team.object
-      saves.push(team.save())
-    }
-  })
-
-  await Promise.all(saves)
-
-  return knex.schema.table('teams', table => {
-    table.dropColumn('object')
-  })
-}
diff --git a/server/model-team/src/migrations/1548328420-add-alias-migration.js b/server/model-team/src/migrations/1548328420-add-alias-migration.js
deleted file mode 100644
index fdb4033401c1398bf9716f1f361972eb2b8663c0..0000000000000000000000000000000000000000
--- a/server/model-team/src/migrations/1548328420-add-alias-migration.js
+++ /dev/null
@@ -1,17 +0,0 @@
-exports.up = async knex => {
-  await knex.schema.createTable('aliases', table => {
-    table.uuid('id').primary()
-    table.timestamp('created').defaultTo(knex.fn.now())
-    table.timestamp('updated').defaultTo(knex.fn.now())
-    table.string('name')
-    table.string('email')
-    table.string('aff')
-  })
-
-  await knex.schema.table('team_members', table => {
-    table
-      .uuid('alias_id')
-      .references('id')
-      .inTable('aliases')
-  })
-}
diff --git a/server/model-team/src/migrations/1596830548-initial-team-migration.sql b/server/model-team/src/migrations/1596830548-initial-team-migration.sql
new file mode 100644
index 0000000000000000000000000000000000000000..c9985aef53957d6be5cbe08da79981325289c2cf
--- /dev/null
+++ b/server/model-team/src/migrations/1596830548-initial-team-migration.sql
@@ -0,0 +1,36 @@
+CREATE TABLE teams (
+  id UUID PRIMARY KEY,
+  created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT current_timestamp,
+  updated TIMESTAMP WITH TIME ZONE,
+
+  name TEXT,
+  role TEXT NOT NULL,
+  members JSONB,
+  owners JSONB,
+  global BOOLEAN,
+  type TEXT NOT NULL,
+  manuscript_id UUID REFERENCES manuscripts(id) ON DELETE CASCADE
+);
+
+CREATE INDEX ON teams (manuscript_id);
+
+CREATE TABLE aliases (
+    id uuid NOT NULL PRIMARY KEY,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    name TEXT,
+    email TEXT,
+    aff TEXT
+);
+
+CREATE TABLE team_members (
+    id uuid NOT NULL PRIMARY KEY,
+    created timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    updated timestamp with time zone DEFAULT CURRENT_TIMESTAMP,
+    status character varying(255),
+    team_id uuid REFERENCES teams(id) ON UPDATE CASCADE ON DELETE CASCADE,
+    user_id uuid REFERENCES users(id) ON DELETE CASCADE,
+    alias_id uuid REFERENCES aliases(id)
+);
+
+CREATE INDEX ON team_members (team_id, user_id);
\ No newline at end of file
diff --git a/server/model-team/src/team.js b/server/model-team/src/team.js
index 1653eb844c1e40260f6ad07db73106700baa3342..dde11acd8c0d663eb4ef42b2ad5d7a5395e61870 100644
--- a/server/model-team/src/team.js
+++ b/server/model-team/src/team.js
@@ -12,7 +12,7 @@ class Team extends BaseModel {
   }
 
   static get relationMappings() {
-    const { Alias, TeamMember, User } = require('@pubsweet/models')
+    const { Alias, TeamMember, User, Manuscript } = require('@pubsweet/models')
 
     return {
       members: {
@@ -49,14 +49,21 @@ class Team extends BaseModel {
           to: 'aliases.id',
         },
       },
+      manuscript: {
+        relation: BaseModel.BelongsToOneRelation,
+        modelClass: Manuscript,
+        join: {
+          from: 'manuscripts.id',
+          to: 'teams.manuscript_id',
+        },
+      },
     }
   }
 
   static get schema() {
     return {
       properties: {
-        objectId: { type: ['string', 'null'], format: 'uuid' },
-        objectType: { type: ['string', 'null'] },
+        manuscriptId: { type: ['string', 'null'], format: 'uuid' },
         name: { type: 'string' },
         role: { type: ['string'] },
         owners: {
diff --git a/server/model-user/src/user.js b/server/model-user/src/user.js
index fd8b6986db9db2826cfd01a66419c5f3f4686e72..02ac1503282761000857809eb9ed7ebc16128c50 100644
--- a/server/model-user/src/user.js
+++ b/server/model-user/src/user.js
@@ -79,22 +79,17 @@ class User extends BaseModel {
     }
   }
 
-  // // eslint-disable-next-line class-methods-use-this
-  // setOwners() {
-  //   // FIXME: this is overriden to be a no-op, because setOwners() is called by
-  //   // the API on create for all entity types and setting `owners` on a User is
-  //   // not allowed. This should instead be solved by having separate code paths
-  //   // in the API for different entity types.
-  // }
-
   // This gives a view of the teams and team member structure to reflect
   // the current roles the user is performing. E.g. if they are a member
   // of a reviewer team and have the status of 'accepted', they will
   // have a 'accepted:reviewer' role present in the returned object
-  async currentRoles(object) {
+  async currentRoles(manuscript) {
     let teams
-    if (object && object.id) {
-      teams = await this.$relatedQuery('teams').where('objectId', object.id)
+    if (manuscript && manuscript.id) {
+      teams = await this.$relatedQuery('teams').where(
+        'manuscriptId',
+        manuscript.id,
+      )
     } else {
       teams = await this.$relatedQuery('teams')
     }
@@ -104,10 +99,10 @@ class User extends BaseModel {
       const role = `${t.status ? `${t.status}:` : ''}${t.role}`
 
       // If there's an existing role for this object, add to the list
-      if (t.objectId && Array.isArray(roles[t.objectId])) {
-        roles[t.objectId].push(role)
-      } else if (t.objectId) {
-        roles[t.objectId] = [role]
+      if (t.manuscriptId && Array.isArray(roles[t.manuscriptId])) {
+        roles[t.manuscriptId].push(role)
+      } else if (t.manuscriptId) {
+        roles[t.manuscriptId] = [role]
       }
     })
     return Object.keys(roles).map(id => ({ id, roles: roles[id] }))