From 7831d7a03cf660556c390c3daa1da5177c03059e Mon Sep 17 00:00:00 2001
From: Alf Eaton <eaton.alf@gmail.com>
Date: Wed, 28 Jun 2017 12:14:34 +0100
Subject: [PATCH] Add child routes to Project

---
 app/components/Project.js   | 40 ++++++++++--------
 app/components/Reviewers.js | 81 +++++++++++++++++++++++++++++++++++++
 app/components/Snapshots.js | 29 ++++++++-----
 app/routes.jsx              | 17 +++++---
 config/shared.js            |  3 +-
 package.json                |  3 +-
 yarn.lock                   | 14 +------
 7 files changed, 138 insertions(+), 49 deletions(-)
 create mode 100644 app/components/Reviewers.js

diff --git a/app/components/Project.js b/app/components/Project.js
index e7436cb1f..161bbb33b 100644
--- a/app/components/Project.js
+++ b/app/components/Project.js
@@ -1,11 +1,10 @@
 import React from 'react'
 import PropTypes from 'prop-types'
 import { connect } from 'react-redux'
-import { bindActionCreators } from 'redux'
-import { browserHistory } from 'react-router'
+import { browserHistory, Link } from 'react-router'
 import { Button } from 'react-bootstrap'
-import actions from 'pubsweet-client/src/actions'
-import Snapshots from './Snapshots'
+import { deleteCollection, getCollection } from 'pubsweet-client/src/actions/collections'
+import { getFragments } from 'pubsweet-client/src/actions/fragments'
 import './Project.css'
 
 class Project extends React.Component {
@@ -24,26 +23,26 @@ class Project extends React.Component {
   }
 
   fetch (id) {
-    const { actions } = this.props
+    const { getCollection, getFragments } = this.props
 
-    actions.getCollection({ id })
-    actions.getFragments({ id })
+    getCollection({ id })
+    getFragments({ id })
   }
 
   remove = () => {
-    const { project, actions } = this.props
+    const { project, deleteCollection } = this.props
 
     if (!window.confirm('Delete this submission?')) {
       return
     }
 
-    actions.deleteCollection(project).then(() => {
+    deleteCollection(project).then(() => {
       browserHistory.push('/')
     })
   }
 
   render () {
-    const { project } = this.props
+    const { project, children } = this.props
 
     if (!project) return null
 
@@ -52,11 +51,13 @@ class Project extends React.Component {
         <div className="container">
           <Button bsSize="small" bsStyle="link" onClick={this.remove} style={{ color: '#eee', background: 'none', position: 'fixed', top: 50, right: 50 }}><span className="fa fa-remove"/></Button>
 
-          <div className="project-title">{project.title}</div>
+          <div className="project-title">
+            <Link to={`/projects/${project.id}`}>{project.title}</Link>
+          </div>
 
           <div style={{ display: 'flex' }}>
             <div style={{ flex: 1 }}>
-              <Snapshots project={project}/>
+              {children}
             </div>
 
             <div className="content-metadata" style={{ width: 200 }}>
@@ -75,16 +76,21 @@ class Project extends React.Component {
 }
 
 Project.propTypes = {
-  actions: PropTypes.object.isRequired,
+  children: PropTypes.node,
   params: PropTypes.object.isRequired,
-  project: PropTypes.object
+  project: PropTypes.object,
+  getFragments: PropTypes.func.isRequired,
+  deleteCollection: PropTypes.func.isRequired,
+  getCollection: PropTypes.func.isRequired
 }
 
 export default connect(
   (state, ownProps) => ({
     project: state.collections.find(collection => collection.id === ownProps.params.project)
   }),
-  dispatch => ({
-    actions: bindActionCreators(actions, dispatch)
-  })
+  {
+    getFragments,
+    deleteCollection,
+    getCollection
+  }
 )(Project)
diff --git a/app/components/Reviewers.js b/app/components/Reviewers.js
new file mode 100644
index 000000000..bff6aabfd
--- /dev/null
+++ b/app/components/Reviewers.js
@@ -0,0 +1,81 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import { connect } from 'react-redux'
+import { updateCollection } from 'pubsweet-client/src/actions/collections'
+import FRC from 'formsy-react-components'
+import { Button } from 'react-bootstrap'
+import uuid from 'uuid'
+
+class Reviewers extends React.Component {
+  addReviewer = data => {
+    const { project, updateCollection } = this.props
+
+    const reviewers = project.reviewers || {}
+
+    const id = 'reviewer-' + uuid()
+
+    reviewers[id] = data
+
+    updateCollection({
+      id: project.id,
+      reviewers
+    })
+  }
+
+  render () {
+    const { project } = this.props
+
+    if (!project) return null
+
+    // TODO: only return reviewer details from the server to authorised users
+
+    return (
+      <div>
+        <h1>Reviewers</h1>
+
+        {project.reviewers && (
+          <div>
+            {Object.keys(project.reviewers).map(key => {
+              const reviewer = project.reviewers[key]
+
+              return (
+                <div key={key}>{reviewer.name} {reviewer.email}</div>
+              )
+            })}
+          </div>
+        )}
+
+        <div className="content-interactive">
+          <FRC.Form onSubmit={this.addReviewer} validateOnSubmit={true} layout="vertical">
+            <div>
+              <FRC.Input type="text" name="name" label="Reviewer name"/>
+            </div>
+
+            <div>
+              <FRC.Input type="email" name="email" label="Reviewer email"/>
+            </div>
+
+            <div style={{ marginTop: 20 }}>
+              <Button type="submit" bsStyle="primary">Add reviewer</Button>
+            </div>
+          </FRC.Form>
+        </div>
+      </div>
+    )
+  }
+}
+
+Reviewers.propTypes = {
+  params: PropTypes.object.isRequired,
+  project: PropTypes.object,
+  updateCollection: PropTypes.func.isRequired
+}
+
+export default connect(
+  (state, ownProps) => ({
+    project: state.collections.find(collection => collection.id === ownProps.params.project)
+  }),
+  {
+    updateCollection
+  }
+)(Reviewers)
diff --git a/app/components/Snapshots.js b/app/components/Snapshots.js
index 3ad94e08e..5ed3d9346 100644
--- a/app/components/Snapshots.js
+++ b/app/components/Snapshots.js
@@ -2,9 +2,9 @@ import React from 'react'
 import PropTypes from 'prop-types'
 import { Link } from 'react-router'
 import { connect } from 'react-redux'
-import { bindActionCreators } from 'redux'
 import moment from 'moment'
-import actions from 'pubsweet-client/src/actions'
+import { updateCollection } from 'pubsweet-client/src/actions/collections'
+import { updateFragment } from 'pubsweet-client/src/actions/fragments'
 import ProjectDeclarations from './ProjectDeclarations'
 import ProjectDeclarationAnswers from './ProjectDeclarationAnswers'
 
@@ -14,9 +14,9 @@ const formatDate = date => moment(date).format('YYYY-MM-DD')
 
 class Snapshots extends React.Component {
   submit = (snapshot) => {
-    const { project, actions } = this.props
+    const { project, updateFragment, updateCollection } = this.props
 
-    actions.updateFragment(project, {
+    updateFragment(project, {
       id: snapshot.id,
       submitted: Date.now()
     })
@@ -24,12 +24,13 @@ class Snapshots extends React.Component {
     project.status = 'submitted'
     project.statusDate = Date.now()
 
-    actions.updateCollection(project)
+    updateCollection(project)
   }
 
   render () {
     const { project, snapshots } = this.props
 
+    if (!project) return null
     if (!snapshots.length) return null
 
     // TODO: only display "submit for review" once declarations are complete
@@ -67,16 +68,22 @@ class Snapshots extends React.Component {
 }
 
 Snapshots.propTypes = {
-  actions: PropTypes.object.isRequired,
   project: PropTypes.object.isRequired,
-  snapshots: PropTypes.array.isRequired
+  snapshots: PropTypes.array.isRequired,
+  updateCollection: PropTypes.func.isRequired,
+  updateFragment: PropTypes.func.isRequired
 }
 
 export default connect(
   (state, ownProps) => ({
-    snapshots: ownProps.project.fragments.map(id => state.fragments[id]).filter(fragment => fragment) // TODO: there shouldn't be any missing
+    project: state.collections
+      .find(collection => collection.id === ownProps.params.project),
+    snapshots: state.collections
+      // TODO: collection id on fragment instead
+      .find(collection => collection.id === ownProps.params.project)
+      .fragments.map(id => state.fragments[id])
+      // TODO: there shouldn't be any missing
+      .filter(fragment => fragment)
   }),
-  dispatch => ({
-    actions: bindActionCreators(actions, dispatch)
-  })
+  { updateFragment, updateCollection }
 )(Snapshots)
diff --git a/app/routes.jsx b/app/routes.jsx
index d068fa17e..13e2b458e 100644
--- a/app/routes.jsx
+++ b/app/routes.jsx
@@ -1,5 +1,5 @@
 import React from 'react'
-import { Redirect, Route } from 'react-router'
+import { IndexRoute, Redirect, Route } from 'react-router'
 import Loadable from 'react-loadable'
 import App from './components/app'
 import AuthenticatedContainer from './components/AuthenticatedContainer'
@@ -16,14 +16,19 @@ export default (
     <Route path="/" component={App}>
       <Route component={AuthenticatedContainer}>
         <Route path="projects" component={chunk(import('./components/ProjectList'))}/>
-        <Route path="projects/:project" component={chunk(import('./components/Project'))}/>
-        <Route path="reviewers/:project" component={chunk(import('./components/Reviewers'))}/>
+
+        <Route path="projects/:project" component={chunk(import('./components/Project'))}>
+          <IndexRoute component={chunk(import('./components/Snapshots'))}/>
+
+          <Route path="reviewers" component={chunk(import('./components/Reviewers'))}/>
+        </Route>
+
         <Route path="editor/:project/:snapshot" component={chunk(import('./components/Editor'))}/>
       </Route>
 
-      <Route path="/signup" component={chunk(import('pubsweet-component-signup/Signup'))}/>
-      <Route path="/login" component={chunk(import('pubsweet-component-login/Login'))}/>
-      <Route path="/password-reset" component={chunk(import('pubsweet-component-password-reset-frontend/PasswordReset'))}/>
+      <Route path="signup" component={chunk(import('pubsweet-component-signup/Signup'))}/>
+      <Route path="login" component={chunk(import('pubsweet-component-login/Login'))}/>
+      <Route path="password-reset" component={chunk(import('pubsweet-component-password-reset-frontend/PasswordReset'))}/>
     </Route>
   </Route>
 )
diff --git a/config/shared.js b/config/shared.js
index 5e0a7e2b8..4a6e9eb81 100644
--- a/config/shared.js
+++ b/config/shared.js
@@ -46,7 +46,8 @@ module.exports = {
       owner: Joi.string().required(),
       status: Joi.string().required(),
       statusDate: Joi.date().timestamp().required(),
-      title: Joi.string().required()
+      title: Joi.string().required(),
+      reviewers: Joi.object()
     },
     fragment: {
       comments: Joi.object(),
diff --git a/package.json b/package.json
index 939ca00c1..a8def87a4 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,8 @@
     "redux": "^3.6.0",
     "redux-logger": "^3.0.1",
     "typeface-fira-sans-condensed": "^0.0.22",
-    "typeface-vollkorn": "^0.0.22"
+    "typeface-vollkorn": "^0.0.22",
+    "uuid": "^3.1.0"
   },
   "devDependencies": {
     "app-module-path": "^2.2.0",
diff --git a/yarn.lock b/yarn.lock
index 230c5e24f..250f37ced 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5296,18 +5296,6 @@ pubsweet-component-login@^0.2.4, pubsweet-component-login@^0.2.6:
   version "0.2.6"
   resolved "https://registry.yarnpkg.com/pubsweet-component-login/-/pubsweet-component-login-0.2.6.tgz#4018b3399eee792ee6344a622d460b4e2f352ba6"
 
-pubsweet-component-manage@^0.1.7:
-  version "0.1.7"
-  resolved "https://registry.yarnpkg.com/pubsweet-component-manage/-/pubsweet-component-manage-0.1.7.tgz#71a14cc510d6ac863bc3c340313a78893f74c4bf"
-  dependencies:
-    bootstrap-sass "^3.3.7"
-    font-awesome "^4.7.0"
-    pubsweet-component-navigation "^0.2.4"
-
-pubsweet-component-navigation@^0.2.4:
-  version "0.2.4"
-  resolved "https://registry.yarnpkg.com/pubsweet-component-navigation/-/pubsweet-component-navigation-0.2.4.tgz#9e94000c44ac486a616a59b915b5216cd6546ae1"
-
 pubsweet-component-password-reset-backend@^0.0.3:
   version "0.0.3"
   resolved "https://registry.yarnpkg.com/pubsweet-component-password-reset-backend/-/pubsweet-component-password-reset-backend-0.0.3.tgz#0c28f91124d6ce86437257ca8e46fed02e6e9c52"
@@ -6745,7 +6733,7 @@ utils-merge@1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8"
 
-uuid@^3.0.0, uuid@^3.0.1:
+uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
 
-- 
GitLab