diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 98e8ef6e541e894353f219b3f249bfcdaf957757..38e63ece935111f7c77f35bebd2ebc3469244151 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,9 +17,10 @@ build: 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 build -t $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA . - docker push $IMAGE_ORG/$IMAGE_NAME:$CI_COMMIT_SHA lint: @@ -40,6 +41,18 @@ test: - cd ${HOME} - npm run test +push:latest: + image: docker:latest + stage: staging + script: + - 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 build -t $IMAGE_ORG/$IMAGE_NAME:latest --label COMMIT_SHA=$CI_COMMIT_SHA . + - docker push $IMAGE_ORG/$IMAGE_NAME:latest + only: + - master + # ----------------------------------------------- # xpub-collabra --------------------------------- # ----------------------------------------------- @@ -50,10 +63,11 @@ review:xpub-collabra: variables: PACKAGE_NAME: xpub-collabra FORCE_FRESH_DB: "yes" + REQUIRES_PROVISIONING: "yes" environment: name: $PACKAGE_NAME/review/$CI_COMMIT_REF_NAME # !! kube-lego will fail if domain > 64 chars - url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}" + url: "http://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}" on_stop: stop_review:xpub-collabra except: - master @@ -66,6 +80,7 @@ stop_review:xpub-collabra: stage: review variables: PACKAGE_NAME: xpub-collabra + REQUIRES_PROVISIONING: "yes" GIT_STRATEGY: none environment: name: $PACKAGE_NAME/review/$CI_COMMIT_REF_NAME @@ -76,7 +91,7 @@ stop_review:xpub-collabra: script: - source deploy.sh - delete_deployment - + - delete_objects_in_environment pvc staging:xpub-collabra: image: pubsweet/deployer:latest @@ -131,8 +146,8 @@ demo:xpub-collabra: # PACKAGE_NAME: xpub-ui # environment: # name: $PACKAGE_NAME/review/$CI_COMMIT_REF_NAME -# # !! kube-lego will fail if domain > 64 chars -# url: "https://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}" +# # !! kube-lego will fail if domain > 63 chars +# url: "http://${CI_ENVIRONMENT_SLUG}.${BASE_DOMAIN}" # on_stop: stop_review:xpub-ui # except: # - master diff --git a/packages/component-dashboard/src/components/Dashboard.js b/packages/component-dashboard/src/components/Dashboard.js index 4cb225f0c1fabaccf9e0923957ef590f51730c7a..5a155027ce69538af01ff6bbcea2a0a080fa8421 100644 --- a/packages/component-dashboard/src/components/Dashboard.js +++ b/packages/component-dashboard/src/components/Dashboard.js @@ -42,7 +42,12 @@ const Dashboard = ({ <div className={classes.heading}>My Submissions</div> {dashboard.owner.map(project => ( <OwnerItemWithVersion - deleteProject={deleteProject} + deleteProject={() => + // eslint-disable-next-line no-alert + window.confirm( + 'Are you sure you want to delete this submission?', + ) && deleteProject(project) + } key={project.id} project={project} /> diff --git a/packages/component-dashboard/src/components/Dashboard.local.scss b/packages/component-dashboard/src/components/Dashboard.local.scss index 2612ee6232a5fcf5c0aac0f2b60d2c0de3c991d4..5b9d944a864a1b4f5b4337bf5b2449a85914c791 100644 --- a/packages/component-dashboard/src/components/Dashboard.local.scss +++ b/packages/component-dashboard/src/components/Dashboard.local.scss @@ -16,7 +16,8 @@ color: var(--color-primary); font-family: Vollkorn, serif; font-size: 1.6em; - margin: 4em 0 2em; + // margin: 4em 0 2em; + margin: 1em 0 1em; text-transform: uppercase; } @@ -26,7 +27,7 @@ } .section:not(:last-of-type) { - margin-bottom: 20px; + margin-bottom: 70px; } a { diff --git a/packages/component-dashboard/src/components/DashboardPage.js b/packages/component-dashboard/src/components/DashboardPage.js index e8efaac3c80fbda1ddd7a9bd0a646e89b57f4505..d2e4f0b7b2149c3731d18d05b38ca90b4f742e5d 100644 --- a/packages/component-dashboard/src/components/DashboardPage.js +++ b/packages/component-dashboard/src/components/DashboardPage.js @@ -29,40 +29,46 @@ export default compose( connect( state => { const { collections } = state - const { conversion, teams } = state + // const { conversion, teams } = state + const { conversion } = state const currentUser = selectCurrentUser(state) const sortedCollections = newestFirst(collections) - const unassignedCollections = sortedCollections.filter( - collection => - collection.status === 'submitted' && - !teams.some( - team => - team.object.type === 'collection' && - team.object.id === collection.id && - team.teamType.name === 'handlingEditor', - ), - ) - const myCollections = teams - .filter( - team => - team.group === 'editor' && - team.object.type === 'collection' && - team.members.includes(currentUser.id), - ) - .map(team => - collections.find(collection => collection.id === team.object.id), - ) + // const unassignedCollections = sortedCollections.filter( + // collection => + // collection.status === 'submitted' && + // !teams.some( + // team => + // team.object.type === 'collection' && + // team.object.id === collection.id && + // team.teamType.name === 'handlingEditor', + // ), + // ) + // const myCollections = teams + // .filter( + // team => + // team.group === 'editor' && + // team.object.type === 'collection' && + // team.members.includes(currentUser.id), + // ) + // .map(team => + // collections.find(collection => collection.id === team.object.id), + // ) const dashboard = { - editor: newestFirst( - unassignedCollections - .concat(myCollections) - .filter( - (collection, index, items) => - items.findIndex(item => item.id === collection.id) === index, - ), + // editor: newestFirst( + // unassignedCollections + // .concat(myCollections) + // .filter( + // (collection, index, items) => + // items.findIndex(item => item.id === collection.id) === index, + // ), + // ), + editor: sortedCollections.filter( + collection => + collection.status === 'submitted' || + collection.status === 'revising', ), owner: sortedCollections.filter( collection => diff --git a/packages/component-dashboard/src/components/Status.js b/packages/component-dashboard/src/components/Status.js index cbac37d53e82b41b7dc501e173a0e31c9480d403..928e602f73c00fd68da3d2deb6327988fafb7d27 100644 --- a/packages/component-dashboard/src/components/Status.js +++ b/packages/component-dashboard/src/components/Status.js @@ -8,7 +8,9 @@ const labels = { assignedToEditor: 'Assigned to editor', assigningReviewers: 'Assigning reviewers', new: 'Unsubmitted', + rejected: 'Rejected', submitted: 'Submitted', + revising: 'Under Revision', } const Status = ({ status }) => ( diff --git a/packages/component-dashboard/src/components/UploadManuscript.js b/packages/component-dashboard/src/components/UploadManuscript.js index 0c66d43e0d7c15e6457c9cdd65cd0cd3300f331c..d7c195941d584c50345f4db32ba8319721fb9ee6 100644 --- a/packages/component-dashboard/src/components/UploadManuscript.js +++ b/packages/component-dashboard/src/components/UploadManuscript.js @@ -1,45 +1,88 @@ -import React from 'react' +import React, { Component } from 'react' import Dropzone from 'react-dropzone' import classnames from 'classnames' import { Icon } from '@pubsweet/ui' import classes from './UploadManuscript.local.scss' -const isIdle = conversion => - !(conversion.converting || conversion.complete || conversion.error) - -const UploadManuscript = ({ uploadManuscript, conversion }) => ( - <Dropzone - accept="application/vnd.openxmlformats-officedocument.wordprocessingml.document" - className={classes.dropzone} - onDrop={uploadManuscript} - > - <div className={classes.root}> - <div - className={classnames({ - [classes.idle]: isIdle(conversion), - [classes.converting]: conversion.converting, - [classes.error]: conversion.error, - [classes.complete]: conversion.complete, - })} +const isIdle = conversion => !(conversion.converting || conversion.error) + +class UploadManuscript extends Component { + constructor(props) { + super(props) + this.state = { + completed: false, + error: false, + } + this.showErrorAndHide = this.showErrorAndHide.bind(this) + } + + componentWillReceiveProps(nextProps) { + if ( + this.props.conversion.converting !== nextProps.conversion.converting && + this.props.conversion.converting === true + ) { + this.setState({ + completed: true, + error: false, + }) + } + + if (nextProps.conversion.error !== undefined) { + this.showErrorAndHide() + } + } + + showErrorAndHide() { + this.setState({ + error: true, + completed: false, + }) + setTimeout(() => { + this.setState({ + error: false, + completed: false, + }) + }, 3000) + } + + render() { + const { uploadManuscript, conversion } = this.props + return ( + <Dropzone + accept="application/vnd.openxmlformats-officedocument.wordprocessingml.document" + className={classes.dropzone} + onDrop={uploadManuscript} > - <span className={classes.icon}> - <Icon color="var(--color-primary)"> - {conversion.complete ? 'check_circle' : 'plus_circle'} - </Icon> - </span> - </div> - - <div className={classes.main}> - {conversion.error ? ( - <div className={classes.error}>{conversion.error.message}</div> - ) : ( - <div className={classes.info}> - {conversion.complete ? 'Submission created' : 'Create submission'} + <div className={classes.root}> + <div + className={classnames({ + [classes.idle]: isIdle(conversion), + [classes.converting]: conversion.converting, + [classes.error]: this.state.error, + })} + > + <span className={classes.icon}> + <Icon color="var(--color-primary)"> + {this.state.completed ? 'check_circle' : 'plus_circle'} + </Icon> + </span> + </div> + + <div className={classes.main}> + {this.state.error ? ( + <div className={classes.error}>{conversion.error.message}</div> + ) : ( + <div className={classes.info}> + {this.state.completed + ? 'Submission created' + : 'Create submission'} + </div> + )} </div> - )} - </div> - </div> - </Dropzone> -) + </div> + </Dropzone> + ) + } +} export default UploadManuscript diff --git a/packages/component-dashboard/src/components/metadata/MetadataOwners.js b/packages/component-dashboard/src/components/metadata/MetadataOwners.js index 40bb90e45ccbb5ca2b6e8abc2302c37361ae5aaa..6142add1bd2749fb34b3e85520d1e637886f2316 100644 --- a/packages/component-dashboard/src/components/metadata/MetadataOwners.js +++ b/packages/component-dashboard/src/components/metadata/MetadataOwners.js @@ -4,7 +4,7 @@ const MetadataOwners = ({ owners }) => ( <span> {owners.map((owner, index) => [ index === 0 ? null : <span>, </span>, - <span>{owner.name || 'Anonymous'}</span>, + <span>{owner.username || 'Anonymous'}</span>, ])} </span> ) diff --git a/packages/component-dashboard/src/components/metadata/MetadataReviewType.js b/packages/component-dashboard/src/components/metadata/MetadataReviewType.js index 11522e78d5df54c18c9174e2310687d258277feb..95cec2654b3fb73a8be9b35728125957f06ae268 100644 --- a/packages/component-dashboard/src/components/metadata/MetadataReviewType.js +++ b/packages/component-dashboard/src/components/metadata/MetadataReviewType.js @@ -1,7 +1,7 @@ import React from 'react' -const MetadataReviewType = ({ openReview }) => ( - <span>{openReview ? 'Open review' : 'Closed review'}</span> +const MetadataReviewType = ({ openPeerReview }) => ( + <span>{openPeerReview === 'yes' ? 'Open review' : 'Closed review'}</span> ) export default MetadataReviewType diff --git a/packages/component-dashboard/src/components/sections/EditorItem.js b/packages/component-dashboard/src/components/sections/EditorItem.js index 902cec169cb1fffac36a2694f0311ff41b340d1e..6983c73df954d0e805b8354391e0aabf4163a4e8 100644 --- a/packages/component-dashboard/src/components/sections/EditorItem.js +++ b/packages/component-dashboard/src/components/sections/EditorItem.js @@ -11,6 +11,35 @@ import classes from './Item.local.scss' import Reviews from '../Reviews' import VersionTitle from './VersionTitle' +const Actions = ({ project, version }) => ( + <div className={classes.links}> + <div className={classes.link}> + {/* {(!version.decision || + version.decision.status !== 'revising' || + version.decision.status !== 'submitted') && ( + <span> + <ProjectLink page="reviewers" project={project} version={version}> + Assign Reviewers + </ProjectLink> + + <Divider separator="|" /> + </span> + )} */} + + <ProjectLink + id={project.id} + page="decisions" + project={project} + version={version} + > + {version.decision && version.decision.status === 'submitted' + ? `Decision: ${version.decision.recommendation}` + : 'Control Panel'} + </ProjectLink> + </div> + </div> +) + const EditorItem = ({ AssignEditor, project, version, addUserToTeam }) => ( <div className={classes.root}> <div className={classes.header}> @@ -31,32 +60,7 @@ const EditorItem = ({ AssignEditor, project, version, addUserToTeam }) => ( <div className={classes.main}> <VersionTitle className={classes.versionTitle} version={version} /> - - <div className={classes.links}> - <div className={classes.link}> - {(!version.decision || version.decision.status !== 'submitted') && ( - <span> - <ProjectLink page="reviewers" project={project} version={version}> - Assign Reviewers - </ProjectLink> - - <Divider separator="|" /> - </span> - )} - - <ProjectLink - id={project.id} - page="decisions" - project={project} - version={version} - > - {version.decision && version.decision.status === 'submitted' - ? `Decision: ${version.decision.recommendation}` - : 'Make decision'} - </ProjectLink> - </div> - </div> - + <Actions project={project} version={version} /> <div className={classes.actions} /> </div> diff --git a/packages/component-dashboard/src/components/sections/Item.local.scss b/packages/component-dashboard/src/components/sections/Item.local.scss index d80e49421cbfccfadbcadb3a706fb16bab70ede8..493083c404e63106e4e7b5fea44abc85f740c30a 100644 --- a/packages/component-dashboard/src/components/sections/Item.local.scss +++ b/packages/component-dashboard/src/components/sections/Item.local.scss @@ -1,24 +1,31 @@ .root { - margin-bottom: 20px; + margin-bottom: 4em; } .main { - align-items: bottom; + align-items: flex-end; display: flex; - justify-content: flex-start; + justify-content: flex-end; + margin-bottom: 0; + padding-left: 1.5em; } -.main, -.reviews, -.roles { +.reviews { + display: inline-flex; + font-size: 0.9em; + justify-content: flex-end; + margin-bottom: 0.6em; + margin-top: 0.3em; padding-left: 1.5em; } .roles { display: flex; font-size: 0.8em; + margin-bottom: 0.6em; margin-left: 0.5em; - margin-top: 1em; + margin-top: 0; + padding-left: 1.5em; text-transform: uppercase; } @@ -26,34 +33,57 @@ margin-right: 3em; } -// double class name to avoid clash with Viewer .root -// .title.title { .versionTitle { flex: 1; - font-family: Vollkorn, serif; + font-family: var(--font-author), serif; font-size: 1.6em; font-weight: 500; - margin-top: 0.2em; + margin-bottom: 0.2em; + margin-top: 0; overflow: hidden; padding-right: 0.4em; +} - > span { - background: white; - display: inline; - padding-right: 0.3em; - } - - &::after { - color: lightgrey; - content: ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . "". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . "". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . "". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . "". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . "". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . "; - float: left; - font-size: 0.8em; - font-weight: 400; - padding-top: 0.25em; - vertical-align: baseline; - white-space: nowrap; - width: 0; - } +.versionTitle > span { + background: white; + display: inline; + margin-right: 0.3em; +} + +.versionTitle::after { + color: lightgrey; + content: + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . ' + '. . . . . . . . . . . . . . . . . . . . '; + float: left; + font-size: 0.8em; + font-weight: 400; + padding-top: 0.25em; + vertical-align: baseline; + white-space: nowrap; + width: 0; } .header { @@ -73,49 +103,52 @@ } .links { - align-self: flex-end; + align-items: flex-end; display: flex; + justify-content: bottom; + padding-bottom: 0.65em; padding-right: 0; } .link { color: var(--color-primary); cursor: pointer; - display: inline-block; + display: inline-flex; font-size: 0.9em; font-style: italic; - padding: 0 0.3em; - padding-bottom: 0.3em; vertical-align: bottom; +} - &:last-child { - margin-right: 0; - } - - & a:hover { - border-bottom: 2px solid var(--color-primary); - } +.link:last-child { + margin-right: 0; } -.action { - display: inline-block; +.link a { + border-bottom: 2px solid transparent; + color: currentcolor; + font-style: inherit; + font-weight: inherit; + margin-right: 0; + text-decoration: none; } -.action button { - background: none; - border: none; - cursor: pointer; - text-transform: none; +.link a:hover { + border-bottom: 2px solid var(--color-primary); } -.reviews { - display: inline-flex; - justify-content: flex-end; - margin-bottom: 10px; - margin-top: 10px; +.action { + display: inline-block; } .editors { display: inline-flex; justify-content: flex-start; } + +.status { + color: grey; + font-style: italic; + margin-bottom: 0.7em; + margin-top: 0; + padding-left: 0.2em; +} diff --git a/packages/component-dashboard/src/components/sections/OwnerItem.js b/packages/component-dashboard/src/components/sections/OwnerItem.js index d122fccc2214e4ad47da4c717175febf47c86ee3..6e9fd74500cacea9db295d63852f4d142d2b182d 100644 --- a/packages/component-dashboard/src/components/sections/OwnerItem.js +++ b/packages/component-dashboard/src/components/sections/OwnerItem.js @@ -17,7 +17,7 @@ const OwnerItem = ({ project, version, deleteProject }) => ( <div className={classes.links}> <div className={classes.link}> <ProjectLink page="submit" project={project} version={version}> - Submission + Summary info </ProjectLink> </div> diff --git a/packages/component-dashboard/src/redux/conversion.js b/packages/component-dashboard/src/redux/conversion.js index 575cdbfb94d71a97e16c2e61aa03b068fba7bc7e..7b2563a56b40784f550a8ca44e77a7e40a0b0f73 100644 --- a/packages/component-dashboard/src/redux/conversion.js +++ b/packages/component-dashboard/src/redux/conversion.js @@ -39,6 +39,11 @@ export const uploadManuscript = (acceptedFiles, history) => dispatch => { request.addEventListener('load', event => { if (request.status >= 400) { + dispatch( + uploadManuscriptFailure({ + message: 'There was an error uploading the file', + }), + ) throw new Error('There was an error uploading the file') } @@ -85,17 +90,15 @@ export const uploadManuscript = (acceptedFiles, history) => dispatch => { const route = `/projects/${collection.id}/versions/${ fragment.id }/submit` - // redirect after a short delay window.setTimeout(() => { history.push(route) - }, 1000) + }, 2000) }) }, ) }) .catch(error => { - console.error(error) dispatch(uploadManuscriptFailure(error)) throw error // rethrow }) @@ -105,7 +108,6 @@ export const uploadManuscript = (acceptedFiles, history) => dispatch => { /* reducer */ const initialState = { - complete: undefined, converting: false, error: undefined, } @@ -114,20 +116,18 @@ export default (state = initialState, action) => { switch (action.type) { case UPLOAD_MANUSCRIPT_REQUEST: return { - complete: false, converting: true, error: undefined, } case UPLOAD_MANUSCRIPT_SUCCESS: return { - complete: true, converting: false, + error: undefined, } case UPLOAD_MANUSCRIPT_FAILURE: return { - complete: false, converting: false, error: action.error, } diff --git a/packages/component-review/src/components/DecisionPage.js b/packages/component-review/src/components/DecisionPage.js index 3067b621f2cc90c7f60a5c24843b33191f159a5b..09eac49bce3afdcb7176856faed7f83262750a18 100644 --- a/packages/component-review/src/components/DecisionPage.js +++ b/packages/component-review/src/components/DecisionPage.js @@ -56,7 +56,7 @@ const handleDecision = (project, version) => dispatch => actions.updateCollection({ id: project.id, rev: project.rev, - status: 'revise', + status: 'revising', }), ).then(() => dispatch( diff --git a/packages/component-review/src/components/ReviewPage.js b/packages/component-review/src/components/ReviewPage.js index 1666c989b6a8c2cf0f549cca347ad01791362d2f..3b42c80703c07784bbcb2089be8ce82417fbfef8 100644 --- a/packages/component-review/src/components/ReviewPage.js +++ b/packages/component-review/src/components/ReviewPage.js @@ -78,15 +78,17 @@ export default compose( const version = selectFragment(state, match.params.version) const currentVersion = selectCurrentVersion(state, project) - const handlingEditors = state.teams - .find( - team => - team.object.type === 'collection' && - team.object.id === match.params.project && - team.teamType.name === 'handlingEditor', - ) - .members.map(id => selectUser(state, id)) + let handlingEditors + const editors = state.teams.find( + team => + team.object.type === 'collection' && + team.object.id === match.params.project && + team.teamType.name === 'handlingEditor', + ) + if (editors) { + handlingEditors = editors.members.map(id => selectUser(state, id)) + } const reviewer = getReviewerFromUser(project, currentVersion, currentUser) return { diff --git a/packages/component-review/src/components/decision/DecisionLayout.js b/packages/component-review/src/components/decision/DecisionLayout.js index 295bed8ad929a8878fc3075c6c19a20cbd9660c3..5fb2cf4dc1f7b4d4f500c3dc56ee01b633c3f814 100644 --- a/packages/component-review/src/components/decision/DecisionLayout.js +++ b/packages/component-review/src/components/decision/DecisionLayout.js @@ -1,6 +1,7 @@ import React from 'react' import moment from 'moment' // import classnames from 'classnames' +import { Link } from 'react-router-dom' import SimpleEditor from 'wax-editor-react' import classes from './DecisionLayout.local.scss' import DecisionForm from './DecisionForm' @@ -9,13 +10,19 @@ import ReviewMetadata from '../metadata/ReviewMetadata' import Decision from './Decision' import Tabs from '../tabs/Tabs' +// TODO -- is passing arrays of react components as props an ok practice? +/* + TODO -- should we make an editor for each tab, or should we just rerender + the same one with different content? +*/ + const DecisionLayout = ({ - project, - versions, currentVersion, - valid, handleSubmit, + project, uploadFile, + valid, + versions, }) => { const decisionSections = [] const editorSections = [] @@ -43,7 +50,12 @@ const DecisionLayout = ({ editorSections.push({ content: ( - <SimpleEditor content={version.source} layout="bare" readOnly /> + <SimpleEditor + content={version.source} + key={key} + layout="bare" + readOnly + /> ), key, label, @@ -53,15 +65,21 @@ const DecisionLayout = ({ const { decision } = currentVersion - if (!decision || !decision.submitted) { + if (currentVersion.submitted && (!decision || !decision.submitted)) { const submittedMoment = moment() const key = submittedMoment.format('x') const label = submittedMoment.format('YYYY-MM-DD') - decisionSections.push({ content: ( <div> <ReviewMetadata version={currentVersion} /> + <Link + to={`/projects/${project.id}/versions/${ + currentVersion.id + }/reviewers`} + > + Assign Reviewers + </Link> <DecisionReviews version={currentVersion} /> <DecisionForm decision={decision} @@ -77,7 +95,12 @@ const DecisionLayout = ({ editorSections.push({ content: ( - <SimpleEditor content={currentVersion.source} layout="bare" readOnly /> + <SimpleEditor + content={currentVersion.source} + key={key} + layout="bare" + readOnly + /> ), key, label, diff --git a/packages/component-review/src/components/decision/DecisionLayout.local.scss b/packages/component-review/src/components/decision/DecisionLayout.local.scss index c8832802e5f924b102c8cb1c6b6ffb42e21aa790..e1509364ea73e50bbce14947c71961f96f9785a5 100644 --- a/packages/component-review/src/components/decision/DecisionLayout.local.scss +++ b/packages/component-review/src/components/decision/DecisionLayout.local.scss @@ -1,16 +1,13 @@ .root { - bottom: 0; - display: flex; - left: 0; - overflow: hidden; - position: absolute; - right: 0; - top: 50px; + display: grid; + grid-column-gap: 2em; + grid-template-areas: "manuscript decision files"; + grid-template-columns: minmax(200px, 80ch) minmax(200px, 50ch) minmax(10ch, 10ch); + justify-content: center; } .column { flex: 1; height: 100%; overflow-y: hidden; - padding: 10px; } diff --git a/packages/component-review/src/components/decision/DecisionReview.js b/packages/component-review/src/components/decision/DecisionReview.js index 49b5faea12177acd7b8bbcced2c3cb450d63c108..8de518cf54a406dbd82a9a21b54d7300beb04032 100644 --- a/packages/component-review/src/components/decision/DecisionReview.js +++ b/packages/component-review/src/components/decision/DecisionReview.js @@ -4,39 +4,71 @@ import { withJournal } from 'xpub-journal' import Review from '../review/Review' import classes from './DecisionReview.local.scss' -const DecisionReview = ({ review, reviewer, journal, open, toggleOpen }) => ( - <div> - <div className={classes.heading}> - <span - className={classes.indicator} - style={{ - backgroundColor: review.recommendation - ? journal.recommendations.find( - item => item.value === review.recommendation, - ).color - : 'black', - }} - /> +const ToggleReview = ({ open, toggle }) => ( + <button className={classes.toggle} onClick={toggle}> + {open ? 'Hide' : 'Show'} + </button> +) - <span className={classes.ordinal}>Review {reviewer.ordinal}</span> +const Bullet = ({ journal, recommendation }) => { + const recommendationColor = journal.recommendations.find( + item => item.value === recommendation, + ).color - <span className={classes.name}>{reviewer.name || 'Anonymous'}</span> + return ( + <span + className={classes.indicator} + style={{ + backgroundColor: recommendation ? recommendationColor : 'black', + }} + /> + ) +} - <span className={classes.dots} /> +const ReviewHeading = ({ + journal, + name, + open, + ordinal, + recommendation, + toggleOpen, +}) => ( + <div className={classes.heading}> + <Bullet journal={journal} recommendation={recommendation} /> - <button className={classes.toggle} onClick={toggleOpen}> - {open ? 'Hide' : 'Show'} - </button> - </div> + <span className={classes.ordinal}>Review {ordinal}</span> + <span className={classes.name}>{name || 'Anonymous'}</span> - {open && ( - <div className={classes.review}> - <Review review={review} /> - </div> - )} + <span className={classes.dots} /> + + <ToggleReview open toggle={toggleOpen} /> </div> ) +const DecisionReview = ({ review, reviewer, journal, open, toggleOpen }) => { + const { recommendation } = review.Recommendation + const { name, ordinal } = reviewer + + return ( + <div> + <ReviewHeading + journal={journal} + name={name} + open={open} + ordinal={ordinal} + recommendation={recommendation} + toggleOpen={toggleOpen} + /> + + {open && ( + <div className={classes.review}> + <Review review={review} /> + </div> + )} + </div> + ) +} + export default compose( withJournal, withState('open', 'setOpen', ({ open }) => open), diff --git a/packages/component-review/src/components/decision/DecisionReviews.js b/packages/component-review/src/components/decision/DecisionReviews.js index 659014093459125ac571728e33dccc965b8e5a47..66aada888cfc0fce999d08334cc47044820048c3 100644 --- a/packages/component-review/src/components/decision/DecisionReviews.js +++ b/packages/component-review/src/components/decision/DecisionReviews.js @@ -13,6 +13,7 @@ const DecisionReviews = ({ journal, version }) => ( .map((review, index) => ( <div className={classes.review} key={review.id}> <DecisionReview + open review={review} reviewer={{ name: null, diff --git a/packages/component-review/src/components/review/Review.js b/packages/component-review/src/components/review/Review.js index 27bf6e2f115fe840d271b51b4eaa410a3db7ea6d..5c34514c60dc65029224caac225788bbf8fb4938 100644 --- a/packages/component-review/src/components/review/Review.js +++ b/packages/component-review/src/components/review/Review.js @@ -40,7 +40,9 @@ const Review = ({ review }) => ( <div> <div className={classes.heading}>Recommendation</div> - <div className={classes.recommendation}>{review.recommendation}</div> + <div className={classes.recommendation}> + {review.Recommendation.recommendation} + </div> </div> </div> ) diff --git a/packages/component-review/src/components/review/ReviewLayout.js b/packages/component-review/src/components/review/ReviewLayout.js index 2c762bab596f251dcc4888375c47a8132d89c365..6eae08e98de8120165c453f45ad419a495f425fd 100644 --- a/packages/component-review/src/components/review/ReviewLayout.js +++ b/packages/component-review/src/components/review/ReviewLayout.js @@ -22,9 +22,12 @@ const ReviewLayout = ({ const editorSections = [] versions.forEach(version => { - const review = version.reviewers.find( - review => review.reviewer === reviewer.id, - ) + let review + if (version.reviewers) { + review = version.reviewers.find( + review => review.reviewer === reviewer._reviewer.id, + ) + } if (review && review.submitted) { const submittedMoment = moment(review.submitted) @@ -48,7 +51,12 @@ const ReviewLayout = ({ // TODO: need to include unreviewed versions? editorSections.push({ content: ( - <SimpleEditor content={version.source} layout="bare" readOnly /> + <SimpleEditor + content={version.source} + key={key} + layout="bare" + readOnly + /> ), key, label, @@ -60,7 +68,7 @@ const ReviewLayout = ({ review => review.reviewer === reviewer.id, ) - if (!review || !review.submitted) { + if (currentVersion.submitted && (!review || !review.submitted)) { const submittedMoment = moment() const key = submittedMoment.format('x') const label = submittedMoment.format('YYYY-MM-DD') @@ -86,7 +94,12 @@ const ReviewLayout = ({ editorSections.push({ content: ( - <SimpleEditor content={currentVersion.source} layout="bare" readOnly /> + <SimpleEditor + content={currentVersion.source} + key={key} + layout="bare" + readOnly + /> ), key, label, diff --git a/packages/component-review/src/components/tabs/Tabs.js b/packages/component-review/src/components/tabs/Tabs.js index 19493a37b7aa766c7df2955707c0e428af715386..b62de60d59e4fd2f7c388e59d0e7a03b60f454f8 100644 --- a/packages/component-review/src/components/tabs/Tabs.js +++ b/packages/component-review/src/components/tabs/Tabs.js @@ -9,13 +9,12 @@ class Tabs extends React.Component { super(props) this.state = { - activeKey: null, + activeKey: props.activeKey || null, } } componentDidMount() { const { activeKey } = this.props - this.setState({ activeKey }) } diff --git a/packages/xpub-connect/src/components/ConnectPage.js b/packages/xpub-connect/src/components/ConnectPage.js index 8f5aa92910f6917879e95d1c9c6b0a7f059e61c6..9cdafed26da3fd32b540335a9a241df8ac9fb502 100644 --- a/packages/xpub-connect/src/components/ConnectPage.js +++ b/packages/xpub-connect/src/components/ConnectPage.js @@ -17,7 +17,7 @@ const ConnectPage = requirements => WrappedComponent => { } componentWillReceiveProps = nextProps => { - this.fetch(nextProps) + // this.fetch(nextProps) } fetch({ isAuthenticated }) {