import React from 'react' import { Query, Mutation } from 'react-apollo' import styled from 'styled-components' import ReactHtmlParser from 'react-html-parser' import { th, lighten, darken } from '@pubsweet/ui-toolkit' import { Action, Icon, Button, H1, H2 } from '@pubsweet/ui' import { Loading, LoadingIcon, Buttons, CloseButton, PreviewPage, PreviewPanel, EditPanel, PanelHeader, PanelContent, Toggle as Toggles, } from '../ui' import UploadFiles, { SubmissionTypes, ReviewTypes, AllTypes, } from '../upload-files' import ManuscriptPreview from '../ManuscriptPreview' import PDFViewer from '../pdf-viewer' import { GET_MANUSCRIPT } from '../operations' import SubmissionHeader from '../SubmissionHeader' import HTMLPreview from './HTMLPreview' import ReviewForm from './ReviewForm' import Annotator from './Annotator' import ReviewInstructions from './ReviewInstructions' import { CONVERT_XML, GET_REVIEWS } from './operations' const PreviewPageDiv = styled(PreviewPage)` & > div > div { overflow: auto; } & > div > div > div:last-child { height: calc(100% - (${th('gridUnit')} * 19)); position: relative; } .show-mobile { display: none; } .pad { padding: 0 calc(2 * ${th('gridUnit')}); } @media screen and (max-width: 870px) { .show-mobile { display: inline-block; } .hide-mobile { display: none; } } ` const PreviewPanelHeader = styled(PanelHeader)` height: calc(${th('gridUnit')} * 10); @media screen and (max-width: 870px) { height: auto; } ` const Toggle = styled(Toggles)` height: calc(${th('gridUnit')} * 7); &.hide-mobile button { margin-left: calc(${th('gridUnit')} * 3); } @media screen and (max-width: 870px) { height: auto; margin-bottom: calc(${th('gridUnit')} * 3); } ` const PreviewError = styled.div` border: ${th('borderWidth')} ${th('borderStyle')} ${th('colorError')}; background-color: ${lighten('colorError', 40)}; padding: ${th('gridUnit')} calc(${th('gridUnit')} * 2); margin: ${th('gridUnit')} 0; ` const BannerDiv = styled.div` position: absolute; top: 0; right: calc(${th('gridUnit')} * 4); left: calc(${th('gridUnit')} * 2); z-index: 3; padding: calc(${th('gridUnit')} * 3) calc(${th('gridUnit')} * 2); background-color: ${darken('colorBackgroundHue', 7)}; border: ${th('borderWidth')} ${th('borderStyle')} ${th('colorBorder')}; display: flex; align-items: start; justify-content: space-between; * { font-size: ${th('fontSizeBaseSmall')}; } & > div { display: flex; align-items: flex-start; } ` const AnnotatePDF = Annotator(PDFViewer) const AnnotateHTML = Annotator(HTMLPreview) const Banner = ({ close, open, ...props }) => ( <BannerDiv {...props}> <div> <Icon size={2}>info</Icon> <div> {`Highlight text and click the pencil icon button to create an annotation and report errors.`} <Action onClick={open}> Click here to view detailed instructions. </Action> </div> </div> <CloseButton onClick={close} size={3} /> </BannerDiv> ) class Review extends React.Component { state = { pane: this.props.currentUser.admin ? 'files' : 'web', instruct: true, banner: false, showManuscript: false, showAll: false, } componentDidMount() { const hidePopup = document.cookie.split('; ').reduce((r, v) => { const parts = v.split('=') return parts[0] === 'hide-review-popup' ? decodeURIComponent(parts[1]) : r }, '') if ( hidePopup && ['xml-qa', 'xml-review'].includes(this.props.manuscript.status) ) { this.setState({ instruct: false, banner: true }) } } render() { const { manuscript, currentUser, reviews } = this.props const review = reviews.find(r => !r.deleted) || null const { files: allfiles, status, teams } = manuscript const sourceFile = allfiles.find(f => f.type === 'manuscript') const files = allfiles.filter( f => !f.type || ReviewTypes.some(rev => rev.value === f.type), ) const originalFiles = allfiles.filter(f => SubmissionTypes.some(sub => sub.value === f.type), ) const html = files.find(file => file.type === 'tempHTML') const pdf = files.find(f => f.type === 'pdf4load' || f.type === 'pdf4print') const xml = files.find(f => f.type === 'PMC') || null const reviewer = teams.find(t => t.role === 'reviewer').teamMembers[0] const { banner, pane, instruct, showManuscript, showAll } = this.state if ( !currentUser.admin && !(currentUser.external && manuscript.status === 'xml-qa') && !( currentUser.id === reviewer.user.id && manuscript.status === 'xml-review' ) ) { this.props.history.push('/') return null } return ( <PreviewPageDiv> {instruct && ( <ReviewInstructions close={() => this.setState({ instruct: false })} /> )} <PreviewPanel style={{ flex: showManuscript && '1 1 750px', width: showManuscript && '50%', }} > <div style={{ maxWidth: showManuscript && '750px' }}> <PreviewPanelHeader> <H1>Review web versions of manuscript</H1> </PreviewPanelHeader> <Toggle> {currentUser.admin && ( <Action className={pane === 'files' ? 'current' : ''} onClick={() => this.setState({ pane: 'files' })} > XML files </Action> )} <Action className={pane === 'web' ? 'current' : ''} onClick={() => this.setState({ pane: 'web' })} > Web preview </Action> <Action className={pane === 'pdf' ? 'current' : ''} onClick={() => this.setState({ pane: 'pdf' })} > PDF preview </Action> <Action className={ pane === 'original' ? 'current show-mobile' : 'show-mobile' } onClick={() => this.setState({ pane: 'original' })} primary={pane !== 'original'} > Submitted file </Action> </Toggle> <PanelContent> {pane === 'files' && ( <Mutation mutation={CONVERT_XML} refetchQueries={() => [ { query: GET_MANUSCRIPT, variables: { id: manuscript.id }, }, ]} > {(convertXML, { data }) => { const generatePreviews = async () => { await convertXML({ variables: { id: xml.id }, }) } return ( <React.Fragment> <Buttons left top> <Button disabled={!xml} onClick={generatePreviews}> Regenerate previews </Button> <Button onClick={() => this.setState({ showAll: !showAll })} > {`${showAll ? 'Hide' : 'Show'} submission files`} </Button> </Buttons> {manuscript.formState && ( <PreviewError> {ReactHtmlParser(manuscript.formState)} </PreviewError> )} <UploadFiles checked files={showAll ? allfiles : files} manuscript={manuscript.id} types={showAll ? AllTypes : ReviewTypes} /> <Buttons left top> <Button disabled={!xml} onClick={generatePreviews}> Regenerate previews </Button> <Button onClick={() => this.setState({ showAll: !showAll })} > {`${showAll ? 'Hide' : 'Show'} submission files`} </Button> </Buttons> </React.Fragment> ) }} </Mutation> )} {pane === 'pdf' && pdf && ( <React.Fragment> {status === 'tagging' ? ( <PDFViewer url={pdf.url} /> ) : ( <React.Fragment> {banner && ( <Banner close={() => this.setState({ banner: false })} open={() => this.setState({ instruct: true })} style={{ top: '1.5rem' }} /> )} <AnnotatePDF file={pdf} reload={this.props.reload} revId={(review && review.id) || null} userId={currentUser.id} /> </React.Fragment> )} </React.Fragment> )} {pane === 'web' && html && ( <React.Fragment> {status === 'tagging' ? ( <HTMLPreview url={html.url} /> ) : ( <React.Fragment> {banner && ( <Banner close={() => this.setState({ banner: false })} open={() => this.setState({ instruct: true })} /> )} <AnnotateHTML file={html} reload={this.props.reload} revId={(review && review.id) || null} userId={currentUser.id} /> </React.Fragment> )} </React.Fragment> )} {pane === 'original' && sourceFile && ( <ManuscriptPreview file={sourceFile} /> )} </PanelContent> </div> </PreviewPanel> <EditPanel style={{ flex: showManuscript && '1 1 750px', width: showManuscript && '50%', }} > <div style={{ maxWidth: showManuscript && '750px' }}> <PreviewPanelHeader> <H2>Compare & approve</H2> </PreviewPanelHeader> <Toggle className="hide-mobile"> <Button onClick={() => this.setState({ showManuscript: !showManuscript }) } > {showManuscript ? 'Back to approval form' : 'Compare with submitted file'} </Button> </Toggle> <PanelContent className={showManuscript && 'pad'}> {showManuscript && sourceFile ? ( <ManuscriptPreview file={sourceFile} /> ) : ( <ReviewForm files={originalFiles} manuscript={manuscript} previews={!!html && !!pdf} reviews={reviews} /> )} </PanelContent> </div> </EditPanel> </PreviewPageDiv> ) } } const ReviewWithHeader = SubmissionHeader(Review) const ReviewPage = ({ match, ...props }) => ( <Query fetchPolicy="cache-and-network" query={GET_MANUSCRIPT} variables={{ id: match.params.id }} > {({ data: { manuscript }, loading: loadingOne }) => ( <Query query={GET_REVIEWS} variables={{ manuscriptId: match.params.id }}> {({ data, loading: loadingTwo, refetch }) => { if (loadingOne || !manuscript) { return ( <Loading> <LoadingIcon /> </Loading> ) } return ( <ReviewWithHeader manuscript={manuscript} match={match} reload={() => refetch()} reviews={(data && data.reviewsByMId) || []} {...props} /> ) }} </Query> )} </Query> ) export default ReviewPage