diff --git a/.gitignore b/.gitignore index 1a7ebace04c0de469ef72c307c33ab2e300c40b7..82fedcb8fd2d087b874481324ec78e2a61f23cb6 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,3 @@ uploads/ config/local*.* .vscode/launch.json .DS_Store -cypress/videos diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e2be730f05ccabfb13de8afe062496e7f37368d9..637cf1c84c3abdba95088f07cdd79297d80c8d61 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,9 @@ # CONTRIBUTING -SimpleJ is a manuscript submission system, based on the discontinued [xpub-collabra](https://gitlab.coko.foundation/xpub/xpub) project. +Kotahi is a manuscript submission system, based on the discontinued [xpub-collabra](https://gitlab.coko.foundation/xpub/xpub) project. It is currently under development by the [Coko Foundation](https://coko.foundation/) and is being built with [PubSweet](https://gitlab.coko.foundation/pubsweet/pubsweet). We welcome people of all kinds to join the community and contribute with knowledge, skills, expertise. Everyone is welcome in our chat room (https://mattermost.coko.foundation/coko/channels/town-square). -In order to contribute to SimpleJ, you're expected to follow a few sensible guidelines. +In order to contribute to Kotahi, you're expected to follow a few sensible guidelines. ## Discuss your contribution before you build @@ -13,7 +13,7 @@ For contributions made as discussions and suggestions, you can at any time open ## Branches -We maintain master as the production branch and tag it with release names. If you wish to contribute to SimpleJ then you need to make a branch and then issue a pull request following this procedure: +We maintain master as the production branch and tag it with release names. If you wish to contribute to Kotahi then you need to make a branch and then issue a pull request following this procedure: Create a user account on Coko's GitLab: http://gitlab.coko.foundation Clone master with `git clone git@gitlab.coko.foundation:simplej/simplej.git` Create a new branch and work off of that. Please name the branch which sensibly identifies the feature you are working on. You can push the branch to GitLab at anytime. @@ -39,6 +39,6 @@ We use conventional commits and verify that commit messages match the pattern, y ## Bug reports, feature requests, support questions This is all done through GitLab using their native issue tracker -Visit the master issue tracker for SimpleJ (https://gitlab.coko.foundation/simplej/simplej/issues) +Visit the master issue tracker for Kotahi (https://gitlab.coko.foundation/simplej/simplej/issues) Tag the issue with 'support', 'bug', or 'feature' to identify the nature of your issue diff --git a/README.md b/README.md index 4a30fa537414abe5a1499bec49a0fd65a647a061..35136847ac41c3869172da55dcae412e11664046 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -# SimpleJ -SimpleJ is a manuscript submission system, based on the discontinued [xpub-collabra](https://gitlab.coko.foundation/xpub/xpub) project. +# Kotahi + +Kotahi is a manuscript submission system, based on the discontinued [xpub-collabra](https://gitlab.coko.foundation/xpub/xpub) project. It is currently under development by the [Coko Foundation](https://coko.foundation/) and is being built with [Pubsweet](https://gitlab.coko.foundation/pubsweet/pubsweet). ## Installation @@ -9,6 +10,7 @@ Developer beware! This project is currently under very heavy development, so thi ### Running the app Start with installing the dependencies: + ``` yarn ``` @@ -17,9 +19,9 @@ Create the file `local-development.json` inside the `config` folder. ```json { - "pubsweet-server": { - "secret": "<your-secret-here>" - } + "pubsweet-server": { + "secret": "<your-secret-here>" + } } ``` @@ -30,6 +32,7 @@ yarn start:services ``` Now (in a separate terminal) run the server (backend PubSweet app): + ``` yarn pubsweet start:server ``` @@ -41,6 +44,11 @@ docker run -e DATABASE_URL="postgres://yourusername@host.docker.internal/yourdat ``` And in another terminal run the client (webpack-based PubSweet app): + ``` yarn pubsweet start:client ``` + +# Other credits + +The real-time chat functionality was heavily inspired by https://github.com/withspectrum/spectrum, from data model approach to copying and adapting certain bits of their React app. Thank you, Spectrum! diff --git a/app/Root.jsx b/app/Root.jsx index a9129436c9989311c4458aa6cb64dbf087af9709..339ec005f06e24d58dbfd3ca3153d3061c96ccf1 100644 --- a/app/Root.jsx +++ b/app/Root.jsx @@ -1,33 +1,9 @@ /* eslint-disable no-param-reassign */ import React from 'react' -// import { BrowserRouter } from 'react-router-dom' -// import PropTypes from 'prop-types' -// import { ThemeProvider } from 'styled-components' -// import { ApolloProvider } from '@apollo/react-components' -// import { ApolloClient } from 'apollo-client' -// import { WebSocketLink } from 'apollo-link-ws' -// import { split, ApolloLink } from 'apollo-link' -// import { getMainDefinition } from 'apollo-utilities' -// import { setContext } from 'apollo-link-context' -// import { -// InMemoryCache, -// IntrospectionFragmentMatcher, -// } from 'apollo-cache-inmemory' -// import { createUploadLink } from 'apollo-upload-client' -import GlobalStyle from './theme/elements/GlobalStyle' - -// import introspectionQueryResultData from './fragmentTypes.json' - import { BrowserRouter } from 'react-router-dom' import PropTypes from 'prop-types' import { ThemeProvider } from 'styled-components' -import { - ApolloProvider, - ApolloClient, - ApolloLink, - split, - gql, -} from '@apollo/client' +import { ApolloProvider, ApolloClient, ApolloLink, split } from '@apollo/client' // import { ApolloClient } from 'apollo-client' import { WebSocketLink } from '@apollo/client/link/ws' // import { split, ApolloLink } from 'apollo-link' @@ -36,7 +12,7 @@ import { setContext } from '@apollo/client/link/context' import { InMemoryCache } from '@apollo/client/cache' import { createUploadLink } from 'apollo-upload-client' -import { GET_CURRENT_USER } from './queries' +import GlobalStyle from './theme/elements/GlobalStyle' import currentRolesVar from './shared/currentRolesVar' // See https://github.com/apollographql/apollo-feature-requests/issues/6#issuecomment-465305186 @@ -101,9 +77,6 @@ const makeApolloClient = (makeConfig, connectToWebSocket) => { const config = { link, cache: new InMemoryCache({ - possibleTypes: { - Identity: ['LocalIdentity', 'ExternalIdentity'], - }, typePolicies: { Manuscript: { fields: { diff --git a/app/components/AdminPage.js b/app/components/AdminPage.js index 114eee5766afe34b8d420fe83fb91e5dca6f6b41..e5b275977e4ca9e4d08720b8d3140435643bce1a 100644 --- a/app/components/AdminPage.js +++ b/app/components/AdminPage.js @@ -70,7 +70,9 @@ const PrivateRoute = ({ component: Component, ...rest }) => ( ) const updateStuff = data => { - currentRolesVar(data.currentUser._currentRoles) + if (data?.currentUser) { + return currentRolesVar(data.currentUser._currentRoles) + } } const AdminPage = ({ children, history, match }) => { diff --git a/app/components/component-chat/src/MentionsInput/MentionsInput.jsx b/app/components/component-chat/src/MentionsInput/MentionsInput.jsx index 3334f25748058f166fb005bc0fbef852d3b97c53..b7a0113fa1a7e0b300c86dab2ab637433c396967 100644 --- a/app/components/component-chat/src/MentionsInput/MentionsInput.jsx +++ b/app/components/component-chat/src/MentionsInput/MentionsInput.jsx @@ -29,7 +29,8 @@ const cleanSuggestionUserObject = user => { ...user, id: user.username, display: user.username, - filterName: (user.name && user.name.toLowerCase()) || user.username.toLowerCase(), + filterName: + (user.name && user.name.toLowerCase()) || user.username.toLowerCase(), } } @@ -82,8 +83,9 @@ const CustomMentionsInput = props => { return } - const cleanSearchUsers = rawSearchUsers.map(user => cleanSuggestionUserObject(user)) - + const cleanSearchUsers = rawSearchUsers.map(user => + cleanSuggestionUserObject(user), + ) // Prepend the filtered participants in case a user is tabbing down right now const fullResults = [...staticSuggestions, ...cleanSearchUsers] @@ -116,9 +118,9 @@ const CustomMentionsInput = props => { > <Mention appendSpaceOnAdd + data={searchUsers} displayTransform={username => `@${username}`} markup="@[__id__]" - data={searchUsers} renderSuggestion={( entry, search, diff --git a/app/components/component-chat/src/Messages/MessageRenderer.jsx b/app/components/component-chat/src/Messages/MessageRenderer.jsx index 03c2f6624f2abfe5113f60d896a2eae51c499844..4e801f4ecc199367a7943299e1b3c43dc0d81929 100644 --- a/app/components/component-chat/src/Messages/MessageRenderer.jsx +++ b/app/components/component-chat/src/Messages/MessageRenderer.jsx @@ -1,118 +1,8 @@ import React from 'react' import ReactMarkdown from 'react-markdown' -import htmlParser from 'react-markdown/plugins/html-parser' -import { useQuery } from '@apollo/client' -import gql from 'graphql-tag' -import styled from 'styled-components' -import { th } from '@pubsweet/ui-toolkit' -import PaperEmbed from './PaperEmbed' - -import { AspectRatio, EmbedContainer, EmbedComponent } from './style' - -const ExternalEmbed = props => { - let { aspectratio, url, src, width = '100%', height = 200 } = props - - if (!src && url) src = url - if (typeof src !== 'string') return null - - // if an aspect ratio is passed in, we need to use the EmbedComponent which does some trickery with padding to force an aspect ratio. Otherwise we should just use a regular iFrame - if (aspectratio && aspectratio !== undefined) { - return ( - <AspectRatio ratio={aspectratio} style={{ height }}> - <EmbedComponent - allowFullScreen - frameBorder="0" - height={height} - src={src} - title={`iframe-${src}`} - width={width} - /> - </AspectRatio> - ) - } - return ( - <EmbedContainer style={{ height }}> - <iframe - allowFullScreen - frameBorder="0" - height={height} - src={src} - title={`iframe-${src}`} - width={width} - /> - </EmbedContainer> - ) -} - -const InternalEmbed = props => { - if (props.entity !== 'thread') return null - return 'INTERNAL' - // return <ThreadAttachment id={props.id} /> -} - -const Embed = props => { - // if (props.type === 'internal') { - // return <InternalEmbed {...props} /> - // } - - // if (props.type === 'paper') { - // return <PaperEmbed {...props} /> - // } - return <></> - // return <ExternalEmbed {...props} /> -} - -// var preprocessingInstructions = [ -// { -// shouldPreprocessNode: function (node) { -// return node.attribs && node.attribs['data-process'] === 'shared'; -// }, -// preprocessNode: function (node) { -// node.attribs = {id: `preprocessed-${node.attribs.id}`,}; -// }, -// } -// ]; -const processingInstructions = [ - { - shouldProcessNode(node) { - return node.name === 'embed' - }, - processNode(node, children, index) { - return <Embed {...node.attribs} id={node.attribs.id} key={index} /> - }, - }, - { - shouldProcessNode(node) { - return node.name === 'p' - }, - processNode(node, children, index) { - return ( - <div {...node.attribs} id={node.attribs.id} key={index}> - {children} - </div> - ) - }, - }, -] - -const parseHtml = htmlParser({ - processingInstructions, -}) - -const MessageRenderer = React.memo(({ message }) => { - const p = props => <div>{props.children}</div> - - return message.enhanced ? ( - <ReactMarkdown - astPlugins={[parseHtml]} - escapeHtml={false} - renderers={{ paragraph: p }} - source={message.enhanced} - /> - ) : ( - <ReactMarkdown source={message.content} /> - ) -}) +const MessageRenderer = React.memo(({ message }) => ( + <ReactMarkdown source={message.content} /> +)) export default MessageRenderer diff --git a/app/components/component-chat/src/Messages/Messages.jsx b/app/components/component-chat/src/Messages/Messages.jsx index 8ca81aaf86f06c4cd7ae419a9e21f30d83cad1f7..89a2fa0bbe7c97685619927bb5e269b808568420 100644 --- a/app/components/component-chat/src/Messages/Messages.jsx +++ b/app/components/component-chat/src/Messages/Messages.jsx @@ -35,12 +35,8 @@ const GET_MESSAGES = gql` profilePicture online defaultIdentity { - ... on ExternalIdentity { - identifier - } - ... on LocalIdentity { - email - } + identifier + email type aff id @@ -69,15 +65,11 @@ const MESSAGES_SUBSCRIPTION = gql` profilePicture online defaultIdentity { - id - ... on ExternalIdentity { - identifier - } - ... on LocalIdentity { - email - } + identifier + email type aff + id name } } @@ -85,7 +77,6 @@ const MESSAGES_SUBSCRIPTION = gql` } ` - const subscribeToNewMessages = (subscribeToMore, channelId) => subscribeToMore({ document: MESSAGES_SUBSCRIPTION, @@ -183,7 +174,10 @@ const Messages = ({ channelId }) => { </NextPageButton> )} {messages && !messages.length && ( - <Placeholder>No discussion for this manuscript yet. Start by typing a message below.</Placeholder> + <Placeholder> + No discussion for this manuscript yet. Start by typing a message + below. + </Placeholder> )} {messages.map(group => { const initialMessage = group[0] @@ -210,7 +204,9 @@ const Messages = ({ channelId }) => { {index === 0 && <UserAvatar user={message.user} />} </GutterContainer> <InnerMessageContainer> - {index === 0 && <Byline>{message.user.defaultIdentity.name}</Byline>} + {index === 0 && ( + <Byline>{message.user.defaultIdentity.name}</Byline> + )} <Bubble> <MessageRenderer message={message} /> </Bubble> diff --git a/app/components/component-chat/src/SuperChatInput/SuperChatInput.jsx b/app/components/component-chat/src/SuperChatInput/SuperChatInput.jsx index eff4db7973a2a3ebc1cba86ba2ba4a15b15a40d3..f84d21a878c6d863cef3989323a7206bba7d0968 100644 --- a/app/components/component-chat/src/SuperChatInput/SuperChatInput.jsx +++ b/app/components/component-chat/src/SuperChatInput/SuperChatInput.jsx @@ -23,7 +23,7 @@ import { RemovePreviewButton, } from './style' -import { CREATE_MESSAGE, GET_MESSAGE_BY_ID } from '../../../../queries' +import { CREATE_MESSAGE } from '../../../../queries' // import sendDirectMessage from 'shared/graphql/mutations/message/sendDirectMessage' // import { getMessageById } from 'shared/graphql/queries/message/getMessage' @@ -101,7 +101,7 @@ export const cleanSuggestionUserObject = user => { const SuperChatInput = props => { const currentUser = useCurrentUser() const [sendChannelMessage] = useMutation(CREATE_MESSAGE) - const [sendDirectMessage] = useMutation(CREATE_MESSAGE) + // const [sendDirectMessage] = useMutation(CREATE_MESSAGE) const cacheKey = `last-content-${props.channelId}` const [text, changeText] = React.useState('') @@ -155,6 +155,7 @@ const SuperChatInput = props => { // If backspace is pressed on the empty case 'Backspace': { if (text.length === 0) removeAttachments() + break } default: } @@ -239,9 +240,9 @@ const SuperChatInput = props => { setMediaPreview(null) setAttachedMediaFile(null) }) - .catch(err => { + .catch(_ => { setIsSendingMediaMessage(false) - props.dispatch(addToastWithTimeout('error', err.message)) + // props.dispatch(addToastWithTimeout('error', err.message)) }) } @@ -271,36 +272,38 @@ const SuperChatInput = props => { } // $FlowFixMe + // eslint-disable-next-line no-unused-vars const [isSendingMediaMessage, setIsSendingMediaMessage] = React.useState( false, ) + // $FlowFixMe const [mediaPreview, setMediaPreview] = React.useState(null) // $FlowFixMe const [mediaFile, setAttachedMediaFile] = React.useState(null) - const previewMedia = blob => { - if (isSendingMediaMessage) return - setIsSendingMediaMessage(true) - setAttachedMediaFile(blob) - inputRef && inputRef.focus() + // const previewMedia = blob => { + // if (isSendingMediaMessage) return + // setIsSendingMediaMessage(true) + // setAttachedMediaFile(blob) + // inputRef && inputRef.focus() - const reader = new FileReader() - reader.onload = () => { - setMediaPreview(reader.result.toString()) - setIsSendingMediaMessage(false) - } + // const reader = new FileReader() + // reader.onload = () => { + // setMediaPreview(reader.result.toString()) + // setIsSendingMediaMessage(false) + // } - if (blob) { - reader.readAsDataURL(blob) - } - } + // if (blob) { + // reader.readAsDataURL(blob) + // } + // } const removeQuotedMessage = () => { - if (props.quotedMessage) - props.dispatch( - replyToMessage({ threadId: props.threadId, messageId: null }), - ) + // if (props.quotedMessage) + // props.dispatch( + // replyToMessage({ threadId: props.threadId, messageId: null }), + // ) } const networkDisabled = diff --git a/app/components/component-dashboard/src/components/Dashboard.js b/app/components/component-dashboard/src/components/Dashboard.js index 221cb872ba1a6a73da87ae3ed866fdd4e5fead50..899780e5f54c8e17dc9f24885d091077f2a20d64 100644 --- a/app/components/component-dashboard/src/components/Dashboard.js +++ b/app/components/component-dashboard/src/components/Dashboard.js @@ -5,7 +5,7 @@ import { Button } from '@pubsweet/ui' import queries from '../graphql/queries/' import mutations from '../graphql/mutations/' -import { Container, Heading, HeadingWithAction, Placeholder } from '../style' +import { Container, Placeholder } from '../style' import EditorItem from './sections/EditorItem' import OwnerItem from './sections/OwnerItem' import ReviewerItem from './sections/ReviewerItem' @@ -15,48 +15,28 @@ import { Title, SectionRow, SectionContent, + Heading, + HeadingWithAction, } from '../../../shared' import hasRole from '../../../../shared/hasRole' -const updateReviewer = (proxy, { data: { reviewerResponse } }) => { - const id = reviewerResponse.object.objectId - const data = proxy.readQuery({ - query: queries.dashboard, - variables: { - id, - }, - }) - - const manuscriptIndex = data.manuscripts.findIndex(manu => manu.id === id) - const teamIndex = data.manuscripts[manuscriptIndex].teams.findIndex( - team => team.id === reviewerResponse.id, - ) - - data.manuscripts[manuscriptIndex].teams[teamIndex] = reviewerResponse - proxy.writeQuery({ query: queries.dashboard, data }) -} - const Dashboard = ({ history, ...props }) => { - // const uploadManuscript = upload() - // const [conversion] = useContext(XpubContext) - const { loading, data, error } = useQuery(queries.dashboard) - const [reviewerRespond] = useMutation(mutations.reviewerResponseMutation, { - // variables: { currentUserId, action, teamId }, - update: updateReviewer, - }) + const [reviewerRespond] = useMutation(mutations.reviewerResponseMutation) + const [deleteManuscript] = useMutation(mutations.deleteManuscriptMutation, { - // variables: { id: manuscript.id }, update: (proxy, { data: { deleteManuscript } }) => { const data = proxy.readQuery({ query: queries.dashboard }) - const manuscriptIndex = data.manuscripts.findIndex( - manuscript => manuscript.id === deleteManuscript, + const manuscripts = data.manuscripts.filter( + manuscript => manuscript.id !== deleteManuscript, ) - if (manuscriptIndex > -1) { - data.manuscripts.splice(manuscriptIndex, 1) - proxy.writeQuery({ query: queries.dashboard, data }) - } + proxy.writeQuery({ + query: queries.dashboard, + data: { + manuscripts, + }, + }) }, }) @@ -70,7 +50,12 @@ const Dashboard = ({ history, ...props }) => { ) const toReview = dashboard.filter(submission => - hasRole(submission, 'reviewer'), + hasRole(submission, [ + 'reviewer', + 'invited:reviewer', + 'accepted:reviewer', + 'completed:reviewer', + ]), ) const manuscriptsImEditorOf = dashboard.filter(submission => @@ -90,7 +75,7 @@ const Dashboard = ({ history, ...props }) => { <SectionHeader> <Title>My Submissions</Title> </SectionHeader> - {dashboard.length > 0 ? ( + {mySubmissions.length > 0 ? ( mySubmissions.map(submission => ( <SectionRow key={`submission-${submission.id}`}> <OwnerItem diff --git a/app/components/component-dashboard/src/components/metadata/MetadataSubmittedDate.js b/app/components/component-dashboard/src/components/metadata/MetadataSubmittedDate.js index dc92f04fedbab3666852d671b5b090a2435a33c3..0cdfd16542f867ab35e77c2f20c3dd7bad16af50 100644 --- a/app/components/component-dashboard/src/components/metadata/MetadataSubmittedDate.js +++ b/app/components/component-dashboard/src/components/metadata/MetadataSubmittedDate.js @@ -3,7 +3,7 @@ import Moment from 'react-moment' const MetadataSubmittedDate = ({ submitted }) => ( <span> - <Moment format="YYYY-MM-DD">{submitted}</Moment> + Submitted on <Moment format="YYYY-MM-DD">{submitted}</Moment>. </span> ) diff --git a/app/components/component-dashboard/src/components/sections/EditorItem.js b/app/components/component-dashboard/src/components/sections/EditorItem.js index dcc6d539ea3deaf6f1b557d0fd67f990d67210d4..b6abd5d49a4f11c221ff25e104bd5bffa70c0754 100644 --- a/app/components/component-dashboard/src/components/sections/EditorItem.js +++ b/app/components/component-dashboard/src/components/sections/EditorItem.js @@ -3,15 +3,13 @@ import styled from 'styled-components' import { Action, ActionGroup } from '@pubsweet/ui' import { Item, StatusBadge } from '../../style' import Meta from '../metadata/Meta' -import MetadataSections from '../metadata/MetadataSections' -import MetadataType from '../metadata/MetadataType' -import MetadataReviewType from '../metadata/MetadataReviewType' import MetadataSubmittedDate from '../metadata/MetadataSubmittedDate' import MetadataAuthors from '../metadata/MetadataAuthors' import MetadataStreamLined from '../metadata/MetadataStreamLined' import JournalLink from '../JournalLink' import Reviews from '../Reviews' import VersionTitle from './VersionTitle' +import prettyRoleText from '../../../../../shared/prettyRoleText' const VersionTitleLink = styled(JournalLink)` text-decoration: none; @@ -72,13 +70,7 @@ const EditorItem = ({ version }) => ( {getSubmitedDate(version) ? ( <MetadataSubmittedDate submitted={getSubmitedDate(version).date} /> ) : null} - <MetadataType type={getMetadataObject(version, 'articleType')} /> - <MetadataSections - sections={getMetadataObject(version, 'articleSections')} - /> - <MetadataReviewType - openPeerReview={getDeclarationsObject(version, 'openPeerReview')} - /> + You are {prettyRoleText(version._currentRoles)}. </Meta> </Item> <Item> diff --git a/app/components/component-dashboard/src/components/sections/ReviewerItem.js b/app/components/component-dashboard/src/components/sections/ReviewerItem.js index d2fa115295cfd673c9e8e01e02f6df329b8cba84..9b0eb409865a44725d800f6158fe12965faa3da0 100644 --- a/app/components/component-dashboard/src/components/sections/ReviewerItem.js +++ b/app/components/component-dashboard/src/components/sections/ReviewerItem.js @@ -26,21 +26,6 @@ const ReviewerItem = ({ version, journals, currentUser, reviewerRespond }) => { team.members.find(member => member.user.id === currentUser.id) const status = currentMember && currentMember.status - // Enable that when Team Models is updated - // const { status } = - // getUserFromTeam(version, 'reviewer').filter( - // member => member.id === currentUser.id, - // )[0] || {} - - // const review = - // (version.reviews || []).find( - // review => - // currentUser && - // review.user && - // review.user.id === currentUser.id && - // !review.isDecision, - // ) || {} - return ( <Item> <VersionTitle version={version} /> diff --git a/app/components/component-formbuilder/src/components/ComponentProperties.jsx b/app/components/component-formbuilder/src/components/ComponentProperties.jsx index 88e837b92b464263460634d273572523af769208..ff375d341d4651690209e96dc3f4e4985c11c232 100644 --- a/app/components/component-formbuilder/src/components/ComponentProperties.jsx +++ b/app/components/component-formbuilder/src/components/ComponentProperties.jsx @@ -59,6 +59,7 @@ const ComponentProperties = ({ onChange={event => { let value = {} if (event.target) { + // eslint-disable-next-line prefer-destructuring value = event.target.value } else { value = event diff --git a/app/components/component-formbuilder/src/components/FormBuilder.jsx b/app/components/component-formbuilder/src/components/FormBuilder.jsx index 81eb4d088c96495f99beef64d8d819f84d122c98..b8b6d20202dd47ec8ef3efdb298560e44750ee20 100644 --- a/app/components/component-formbuilder/src/components/FormBuilder.jsx +++ b/app/components/component-formbuilder/src/components/FormBuilder.jsx @@ -1,11 +1,4 @@ import React, { useState } from 'react' -import { - compose, - withState, - withHandlers, - lifecycle, - // setDisplayName, -} from 'recompose' import styled, { withTheme } from 'styled-components' import { unescape } from 'lodash' import { th } from '@pubsweet/ui-toolkit' @@ -141,27 +134,3 @@ const FormBuilder = ({ } export default FormBuilder -// FormBuilder.displayName = 'FormBuilder' - -// export default compose( -// withState('elements', 'onAddElements', ({ form }) => form.children || []), -// withHandlers({ -// addElements: ({ onAddElements, form }) => addElement => -// onAddElements(() => { -// const addEl = { children: form.children || [] } -// addEl.children = [...addEl.children, addElement] -// return addEl.children -// }), -// }), -// lifecycle({ -// componentWillReceiveProps(nextProps) { -// if (this.props.form.children !== nextProps.form.children) { -// this.setState({ elements: nextProps.form.children }) -// } - -// if (this.props.elements !== nextProps.elements) { -// this.setState({ elements: nextProps.elements }) -// } -// }, -// }), -// )(FormBuilder) diff --git a/app/components/component-formbuilder/src/redux/FormBuilder.js b/app/components/component-formbuilder/src/redux/FormBuilder.js index edf54a138bfc7251860e5e3f3e200a684e48433b..26325d6da707cd4c3b480215d436937bdebf7276 100644 --- a/app/components/component-formbuilder/src/redux/FormBuilder.js +++ b/app/components/component-formbuilder/src/redux/FormBuilder.js @@ -1,135 +1,135 @@ -import * as api from 'pubsweet-client/src/helpers/api' - -export const GET_FORM_REQUEST = 'GET_FORM_REQUEST' -export const GET_FORM_SUCCESS = 'GET_FORM_SUCCESS' -export const GET_FORM_FAILURE = 'GET_FORM_FAILURE' - -function getFormRequest(project, version) { - return { - type: GET_FORM_REQUEST, - } -} - -function getFormSuccess(forms) { - return { - type: GET_FORM_SUCCESS, - forms, - } -} - -function getFormFailure(error) { - return { - type: GET_FORM_FAILURE, - error, - } -} - -export function getForms() { - return dispatch => { - dispatch(getFormRequest()) - - return api - .get('/get-forms', {}) - .then(result => { - dispatch(getFormSuccess(result)) - }) - .catch(error => dispatch(getFormFailure(error))) - } -} - -export function getForm(formId) { - return dispatch => { - dispatch(getFormRequest()) - - return api - .get(`/get-form/${formId}`, {}) - .then(result => { - dispatch(getFormSuccess(result)) - }) - .catch(error => dispatch(getFormFailure(error))) - } -} - -export function updateForms(form, properties) { - return dispatch => { - dispatch(getFormRequest()) - - return api - .update(`/update-forms/${form.id}`, properties) - .then(result => { - dispatch(getFormSuccess(result)) - }) - .catch(error => dispatch(getFormFailure(error))) - } -} - -export function updateElements(form, properties) { - return dispatch => { - dispatch(getFormRequest()) - - return api - .update( - `/update-forms/${form.id}/element/${properties.children.id}`, - properties, - ) - .then(result => { - dispatch(getFormSuccess(result)) - }) - .catch(error => dispatch(getFormFailure(error))) - } -} - -export function deleteForms(form) { - return dispatch => { - dispatch(getFormRequest()) - - return api - .remove(`/delete-forms/${form.id}`) - .then(result => { - dispatch(getFormSuccess(result)) - }) - .catch(error => dispatch(getFormFailure(error))) - } -} - -export function deleteElements(form, element) { - return dispatch => { - dispatch(getFormRequest()) - - return api - .remove(`/delete-forms/${form.id}/elements/${element.id}`) - .then(result => { - dispatch(getFormSuccess(result)) - }) - .catch(error => dispatch(getFormFailure(error))) - } -} - -export function createForms(properties) { - return dispatch => { - dispatch(getFormRequest()) - - return api - .create('/create-forms', properties) - .then(result => { - dispatch(getFormSuccess(result)) - }) - .catch(error => dispatch(getFormFailure(error))) - } -} - -const initialState = {} -export default (state = initialState, action) => { - switch (action.type) { - case GET_FORM_SUCCESS: - return { - forms: action.forms.forms, - } - - case GET_FORM_FAILURE: - return { error: action.error } - - default: - return state - } -} +// import * as api from 'pubsweet-client/src/helpers/api' + +// export const GET_FORM_REQUEST = 'GET_FORM_REQUEST' +// export const GET_FORM_SUCCESS = 'GET_FORM_SUCCESS' +// export const GET_FORM_FAILURE = 'GET_FORM_FAILURE' + +// function getFormRequest(project, version) { +// return { +// type: GET_FORM_REQUEST, +// } +// } + +// function getFormSuccess(forms) { +// return { +// type: GET_FORM_SUCCESS, +// forms, +// } +// } + +// function getFormFailure(error) { +// return { +// type: GET_FORM_FAILURE, +// error, +// } +// } + +// export function getForms() { +// return dispatch => { +// dispatch(getFormRequest()) + +// return api +// .get('/get-forms', {}) +// .then(result => { +// dispatch(getFormSuccess(result)) +// }) +// .catch(error => dispatch(getFormFailure(error))) +// } +// } + +// export function getForm(formId) { +// return dispatch => { +// dispatch(getFormRequest()) + +// return api +// .get(`/get-form/${formId}`, {}) +// .then(result => { +// dispatch(getFormSuccess(result)) +// }) +// .catch(error => dispatch(getFormFailure(error))) +// } +// } + +// export function updateForms(form, properties) { +// return dispatch => { +// dispatch(getFormRequest()) + +// return api +// .update(`/update-forms/${form.id}`, properties) +// .then(result => { +// dispatch(getFormSuccess(result)) +// }) +// .catch(error => dispatch(getFormFailure(error))) +// } +// } + +// export function updateElements(form, properties) { +// return dispatch => { +// dispatch(getFormRequest()) + +// return api +// .update( +// `/update-forms/${form.id}/element/${properties.children.id}`, +// properties, +// ) +// .then(result => { +// dispatch(getFormSuccess(result)) +// }) +// .catch(error => dispatch(getFormFailure(error))) +// } +// } + +// export function deleteForms(form) { +// return dispatch => { +// dispatch(getFormRequest()) + +// return api +// .remove(`/delete-forms/${form.id}`) +// .then(result => { +// dispatch(getFormSuccess(result)) +// }) +// .catch(error => dispatch(getFormFailure(error))) +// } +// } + +// export function deleteElements(form, element) { +// return dispatch => { +// dispatch(getFormRequest()) + +// return api +// .remove(`/delete-forms/${form.id}/elements/${element.id}`) +// .then(result => { +// dispatch(getFormSuccess(result)) +// }) +// .catch(error => dispatch(getFormFailure(error))) +// } +// } + +// export function createForms(properties) { +// return dispatch => { +// dispatch(getFormRequest()) + +// return api +// .create('/create-forms', properties) +// .then(result => { +// dispatch(getFormSuccess(result)) +// }) +// .catch(error => dispatch(getFormFailure(error))) +// } +// } + +// const initialState = {} +// export default (state = initialState, action) => { +// switch (action.type) { +// case GET_FORM_SUCCESS: +// return { +// forms: action.forms.forms, +// } + +// case GET_FORM_FAILURE: +// return { error: action.error } + +// default: +// return state +// } +// } diff --git a/app/components/component-login/src/Login.jsx b/app/components/component-login/src/Login.jsx index 9fd99f091f431bfbd4b8bbba646a120f0c80cc52..d04d53a279a966cc029205f90a65debd58ebc2cd 100644 --- a/app/components/component-login/src/Login.jsx +++ b/app/components/component-login/src/Login.jsx @@ -1,13 +1,9 @@ import React, { useState } from 'react' import { Redirect } from 'react-router-dom' -import PropTypes from 'prop-types' -import { withFormik } from 'formik' import config from 'config' import { th, grid, lighten } from '@pubsweet/ui-toolkit' -import { CenteredColumn, H1, Button } from '@pubsweet/ui' +import { H1, Button } from '@pubsweet/ui' import styled from 'styled-components' -import { Section } from '../../shared' -import { Placeholder } from '../../component-chat/src/Messages/style' const getNextUrl = () => { const url = new URL(window.location.href) @@ -21,26 +17,6 @@ const getNextUrl = () => { return `${url.searchParams.get('next') || redirectLink}` } -const localStorage = window.localStorage || undefined - -const handleSubmit = (values, { props, setSubmitting, setErrors }) => - props - .loginUser({ variables: { input: values } }) - .then(({ data, errors }) => { - if (!errors) { - localStorage.setItem('token', data.loginUser.token) - setTimeout(() => { - props.onLoggedIn(getNextUrl()) - }, 100) - } - }) - .catch(e => { - if (e.graphQLErrors && e.graphQLErrors.length > 0) { - setSubmitting(false) - setErrors(e.graphQLErrors[0].message) - } - }) - const getToken = props => { const { location } = props if (location && location.search && location.search.match(/^\?token=/)) { @@ -114,18 +90,11 @@ const StyledORCIDIcon = styled(ORCIDIcon)` ` const Login = ({ logo = null, ...props }) => { - // Is ORCID authentication enabled? - const orcid = - config['pubsweet-component-login'] && - config['pubsweet-component-login'].orcid - const token = getToken(props) // If a JWT token is supplied as a query param (e.g. from OAuth) // go ahead and fetch the redirect URL const initialRedirectLink = token ? getNextUrl() : null - const [redirectLink, setRedirectLink] = useState(initialRedirectLink) - // Also set the redirect link upon successful login (via handleSubmit) - const onLoggedIn = () => setRedirectLink(getNextUrl()) + const [redirectLink] = useState(initialRedirectLink) if (token) { window.localStorage.setItem('token', token) @@ -140,7 +109,7 @@ const Login = ({ logo = null, ...props }) => { <Centered> <Content> {journalName === 'Aperture' && ( - <img src="/public/logo-aperture.png" /> + <img alt="Aperture" src="/public/logo-aperture.png" /> )} <H1>Login to {journalName}</H1> {journalName} uses ORCID <StyledORCIDIcon /> to identify authors and diff --git a/app/components/component-manuscripts/src/Manuscripts.jsx b/app/components/component-manuscripts/src/Manuscripts.jsx index dd3dee928e6f6d45e9e95c9b37c98c6e438ca810..574397c14fe108366d1593f3b0e2ae30c7b07c4f 100644 --- a/app/components/component-manuscripts/src/Manuscripts.jsx +++ b/app/components/component-manuscripts/src/Manuscripts.jsx @@ -42,6 +42,7 @@ const GET_MANUSCRIPTS = gql` username online defaultIdentity { + id name } profilePicture diff --git a/app/components/component-profile/src/FormGrid.jsx b/app/components/component-profile/src/FormGrid.jsx index e9144c8135943ecd708ab0d52327b1eeb93e1837..6c027ed888a3a75d20cef622775de9ecc110c7ce 100644 --- a/app/components/component-profile/src/FormGrid.jsx +++ b/app/components/component-profile/src/FormGrid.jsx @@ -38,4 +38,3 @@ export const FormRow = styled.div` margin-left: calc(${th('gridUnit')} * 2); } ` - diff --git a/app/components/component-profile/src/PageWithHeader.jsx b/app/components/component-profile/src/PageWithHeader.jsx index 28929e72dceb1885f0a18229fe1f63462e69caef..0570a64ea6b405cae75cdc68de9f1c09771c3fd4 100644 --- a/app/components/component-profile/src/PageWithHeader.jsx +++ b/app/components/component-profile/src/PageWithHeader.jsx @@ -50,8 +50,8 @@ const PageWithHeader = ({ children, header }) => ( <Heading>{header}</Heading> </HeaderText> </StyledHeader> - { children } + {children} </Settings> ) -export default PageWithHeader \ No newline at end of file +export default PageWithHeader diff --git a/app/components/component-profile/src/Profile.jsx b/app/components/component-profile/src/Profile.jsx index a137940b612b238a9e0cd3986bba83dadf09d2be..a3ee135b84a5ada6c1615e9eae8a5b49e6a5ff71 100644 --- a/app/components/component-profile/src/Profile.jsx +++ b/app/components/component-profile/src/Profile.jsx @@ -1,5 +1,5 @@ import React, { useCallback } from 'react' -import { Button, Action } from '@pubsweet/ui' +import { Button } from '@pubsweet/ui' // import { th } from '@pubsweet/ui-toolkit' // import styled from 'styled-components' import gql from 'graphql-tag' @@ -19,15 +19,12 @@ const GET_CURRENT_USER = gql` profilePicture username defaultIdentity { + identifier + email + type aff + id name - type - ... on ExternalIdentity { - identifier - } - ... on LocalIdentity { - email - } } } } @@ -111,7 +108,7 @@ const Profile = () => { <ChangeUsername user={data.currentUser} /> </div> </FormRow> - <Button onClick={() => logoutUser()}>Logout</Button> + <Button onClick={() => logoutUser()}>Logout</Button> </FormGrid> </PageWithHeader> </> diff --git a/app/components/component-profile/src/ProfileImage.jsx b/app/components/component-profile/src/ProfileImage.jsx index fbaa86fdcc7eabe886eb3872e1aa8949c07868ea..162c43614fcf99285585085c648c170f0e27175f 100644 --- a/app/components/component-profile/src/ProfileImage.jsx +++ b/app/components/component-profile/src/ProfileImage.jsx @@ -14,4 +14,3 @@ export const SmallProfileImage = styled.img` object-fit: cover; border-radius: 50%; ` - diff --git a/app/components/component-review/src/components/ReviewPage.js b/app/components/component-review/src/components/ReviewPage.js index 99cb6ff80f674da0003f56d42e6b57b74f988944..db9d9a998945b70ad7acb4a26cb4eef358a47f12 100644 --- a/app/components/component-review/src/components/ReviewPage.js +++ b/app/components/component-review/src/components/ReviewPage.js @@ -2,7 +2,7 @@ import React from 'react' import { useMutation, useQuery } from '@apollo/client' import gql from 'graphql-tag' import { Formik } from 'formik' -import { cloneDeep } from 'lodash' +// import { cloneDeep } from 'lodash' import { getCommentContent } from './review/util' import ReviewLayout from '../components/review/ReviewLayout' import { Spinner } from '../../../shared' @@ -34,22 +34,22 @@ const reviewFields = ` } ` -const teamFields = ` - id - name - role - object { - objectId - objectType - } - members { - id - user { - id - username - } - } -` +// const teamFields = ` +// id +// name +// role +// object { +// objectId +// objectType +// } +// members { +// id +// user { +// id +// username +// } +// } +// ` const fragmentFields = ` id @@ -145,10 +145,11 @@ const query = gql` } ` -const updateTeamMutation = gql` - mutation($id: ID!, $input: TeamInput) { - updateTeam(id: $id, input: $input) { - ${teamFields} +const completeReviewMutation = gql` + mutation($id: ID!) { + completeReview(id: $id) { + id + status } } ` @@ -187,12 +188,11 @@ const createFileMutation = gql` export default ({ match, ...props }) => { const currentUser = useCurrentUser() const [updateReviewMutation] = useMutation(updateReviewMutationQuery) + const [completeReview] = useMutation(completeReviewMutation) // File upload // const [uploadReviewFiles] = useMutation(uploadReviewFilesMutation) - const [updateTeam] = useMutation(updateTeamMutation) - const [createFileM] = useMutation(createFileMutation) const createFile = file => createFileM({ @@ -269,24 +269,24 @@ export default ({ match, ...props }) => { 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 }) - }, + // 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 }) + // }, }) } @@ -308,27 +308,14 @@ export default ({ match, ...props }) => { createFile(newFile) }) - const 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({ + const handleSubmit = async ({ reviewId, history }) => { + await completeReview({ variables: { - id: team.id, - input: { - members: team.members, - }, + id: reviewId, }, - }).then(() => { - history.push('/journal/dashboard') }) + + history.push('/journal/dashboard') } return ( @@ -343,7 +330,9 @@ export default ({ match, ...props }) => { recommendation: null, } } - onSubmit={values => completeReview(props.history)} + onSubmit={values => + handleSubmit({ reviewId: review.id, history: props.history }) + } validateOnMount={review => { if (!review.id) return false const hasRecommendation = review.recommendation !== null diff --git a/app/components/component-review/src/components/ReviewersPage.js b/app/components/component-review/src/components/ReviewersPage.js index 6331ff1f7fb497afcc8ad5f89de5ad5bb67da522..266792ad751c51e2beb4f19aae84294521048539 100644 --- a/app/components/component-review/src/components/ReviewersPage.js +++ b/app/components/component-review/src/components/ReviewersPage.js @@ -1,12 +1,9 @@ -import { compose, withProps } from 'recompose' -import { withFormik } from 'formik' -import { graphql } from '@apollo/client/react/hoc' -import gql from 'graphql-tag' -import { withLoader } from 'pubsweet-client' -import { omit } from 'lodash' - +import React from 'react' +import { Formik } from 'formik' +import { gql, useQuery, useMutation } from '@apollo/client' import Reviewers from '../components/reviewers/Reviewers' import ReviewerContainer from '../components/reviewers/ReviewerContainer' +import { Spinner } from '../../../shared' const teamFields = ` id @@ -23,6 +20,10 @@ const teamFields = ` username profilePicture online + defaultIdentity { + id + name + } } status } @@ -68,17 +69,17 @@ const fragmentFields = ` status ` -const createTeamMutation = gql` - mutation($input: TeamInput!) { - createTeam(input: $input) { +const addReviewerMutation = gql` + mutation($manuscriptId: ID!, $userId: ID!) { + addReviewer(manuscriptId: $manuscriptId, userId: $userId) { ${teamFields} } } ` -const updateTeamMutation = gql` - mutation($id: ID, $input: TeamInput) { - updateTeam(id: $id, input: $input) { +const removeReviewerMutation = gql` + mutation($manuscriptId: ID!, $userId: ID!) { + removeReviewer(manuscriptId: $manuscriptId, userId: $userId) { ${teamFields} } } @@ -86,18 +87,16 @@ const updateTeamMutation = gql` const query = gql` query($id: ID!) { - currentUser { - id - username - admin - } - users { id username profilePicture online admin + defaultIdentity { + id + name + } } teams { @@ -110,121 +109,52 @@ const query = gql` } ` -const update = match => (proxy, { data: { updateTeam, createTeam } }) => { - const data = JSON.parse( - JSON.stringify( - proxy.readQuery({ - query, - variables: { - id: match.params.version, - }, - }), - ), - ) +const ReviewersPage = ({ match, history }) => { + const { data, error, loading } = useQuery(query, { + variables: { id: match.params.version }, + }) - if (updateTeam) { - const teamIndex = data.teams.findIndex(team => team.id === updateTeam.id) - const manuscriptTeamIndex = data.manuscript.teams.findIndex( - team => team.id === updateTeam.id, - ) - data.teams[teamIndex] = updateTeam - data.manuscript.teams[manuscriptTeamIndex] = updateTeam - } + const [addReviewer] = useMutation(addReviewerMutation) + const [removeReviewer] = useMutation(removeReviewerMutation) - if (createTeam) { - data.teams.push(createTeam) - data.manuscript.teams.push(createTeam) - } - proxy.writeQuery({ query, data }) -} - -const handleSubmit = ( - { user }, - { props: { manuscript, updateTeamMutation, createTeamMutation, match } }, -) => { - const team = manuscript.teams.find(team => team.role === 'reviewer') || {} - - const teamAdd = { - objectId: manuscript.id, - objectType: 'Manuscript', - // status: [{ user: user.id, status: 'invited' }], - name: 'Reviewers', - role: 'reviewer', - members: [{ user: { id: user.id }, status: 'invited' }], - } - if (team.id) { - const newTeam = { - ...omit(team, ['object', 'id', '__typename']), - // TODO: Find a cleaner way of updating members - members: team.members.map(member => ({ - user: { - id: member.user.id, - }, - status: member.status, - })), - } - - newTeam.members.push({ user: { id: user.id }, status: 'invited' }) - // newTeam.status.push({ user: user.id, status: 'invited' }) - updateTeamMutation({ - variables: { - id: team.id, - input: newTeam, - }, - update: update(match), - }) - } else { - createTeamMutation({ - variables: { - input: teamAdd, - }, - update: update(match), - }) + if (loading) { + return <Spinner /> } + if (error) return error + + const { manuscript, teams, users } = data + const reviewersTeam = + teams.find( + team => + team.role === 'reviewer' && + team.object.objectId === manuscript.id && + team.object.objectType === 'Manuscript', + ) || {} + + const reviewers = reviewersTeam.members || [] + return ( + <Formik + displayName="reviewers" + initialValues={{ user: undefined }} + onSubmit={values => + addReviewer({ + variables: { userId: values.user.id, manuscriptId: manuscript.id }, + }) + } + > + {props => ( + <Reviewers + {...props} + history={history} + manuscript={manuscript} + removeReviewer={removeReviewer} + Reviewer={ReviewerContainer} + reviewers={reviewers} + reviewerUsers={users} + /> + )} + </Formik> + ) } -export default compose( - graphql(query, { - options: ({ match }) => ({ - variables: { - id: match.params.version, - }, - }), - }), - graphql(createTeamMutation, { name: 'createTeamMutation' }), - graphql(updateTeamMutation, { name: 'updateTeamMutation' }), - withLoader(), - withProps( - ({ - manuscript, - teams = [], - users, - match: { - params: { journal }, - }, - }) => { - const reviewersTeam = - teams.find( - team => - team.role === 'reviewer' && - team.object.objectId === manuscript.id && - team.object.objectType === 'Manuscript', - ) || {} - - return { - reviewers: reviewersTeam.members || [], - journal: { id: journal }, - reviewerUsers: users, - Reviewer: ReviewerContainer, - } - }, - ), - // withHandlers({ - // loadOptions: props => props.reviewerUsers, // loadOptions(props), - // }), - withFormik({ - mapPropsToValues: () => ({ user: '' }), - displayName: 'reviewers', - handleSubmit, - }), -)(Reviewers) +export default ReviewersPage diff --git a/app/components/component-review/src/components/assignEditors/AssignEditor.js b/app/components/component-review/src/components/assignEditors/AssignEditor.js index 51cd4ab4d846021f406c697da7a401ad354a768d..6383b954811a57823a7b8481300cbcd52c2a8596 100644 --- a/app/components/component-review/src/components/assignEditors/AssignEditor.js +++ b/app/components/component-review/src/components/assignEditors/AssignEditor.js @@ -8,7 +8,7 @@ import gql from 'graphql-tag' import { withLoader } from 'pubsweet-client' const editorOption = user => ({ - label: user.username, // TODO: name + label: user.defaultIdentity.name, value: user.id, }) @@ -35,6 +35,10 @@ const query = gql` id username admin + defaultIdentity { + id + name + } } } ` diff --git a/app/components/component-review/src/components/decision/DecisionReviews.js b/app/components/component-review/src/components/decision/DecisionReviews.js index e889f256ac2e06777a3a760b81c09d791430145f..b7bcab3bd4252b71f56f752800d75ed56aff5d5e 100644 --- a/app/components/component-review/src/components/decision/DecisionReviews.js +++ b/app/components/component-review/src/components/decision/DecisionReviews.js @@ -44,12 +44,13 @@ const DecisionReviews = ({ manuscript }) => ( </SectionRow> )) ) : ( - <SectionRow> - <Action to={`/journal/versions/${manuscript.id}/reviewers`}> - Assign Reviewers - </Action> - </SectionRow> + <SectionRow>No reviews completed yet.</SectionRow> )} + <SectionRow> + <Action to={`/journal/versions/${manuscript.id}/reviewers`}> + Manage Reviewers + </Action> + </SectionRow> </Container> ) diff --git a/app/components/component-review/src/components/reviewers/ReviewerForm.js b/app/components/component-review/src/components/reviewers/ReviewerForm.js index b2df4cddfeed6af40038ff113ff255a69f8c60ac..595a27a257c74b086321818a44ba8835d54b83cf 100644 --- a/app/components/component-review/src/components/reviewers/ReviewerForm.js +++ b/app/components/component-review/src/components/reviewers/ReviewerForm.js @@ -1,9 +1,11 @@ import React from 'react' -import Select from 'react-select1' import { Field } from 'formik' import { Button } from '@pubsweet/ui' import { required } from 'xpub-validators' -import 'react-select1/dist/react-select.css' +import styled from 'styled-components' +// import 'react-select1/dist/react-select.css' +import { grid } from '@pubsweet/ui-toolkit' +import { Select } from '../../../../shared' const OptionRenderer = option => ( <div> @@ -12,6 +14,11 @@ const OptionRenderer = option => ( </div> ) +const FieldAndButton = styled.div` + display: grid; + grid-template-columns: ${grid(30)} ${grid(10)}; + grid-gap: ${grid(2)}; +` const ReviewerInput = ({ field, form: { values, setFieldValue }, @@ -19,9 +26,10 @@ const ReviewerInput = ({ replace, reviewerUsers, }) => ( - <Select.Creatable + <Select {...field} - labelKey="username" + getOptionLabel={option => option.defaultIdentity?.name} + getOptionValue={option => option.id} onChange={user => { setFieldValue('user', user) }} @@ -40,15 +48,17 @@ const ReviewerForm = ({ reviewerUsers, }) => ( <form onSubmit={handleSubmit}> - <Field - component={ReviewerInput} - name="user" - reviewerUsers={reviewerUsers} - validate={required} - /> - <Button disabled={!isValid} primary type="submit"> - Invite reviewer - </Button> + <FieldAndButton> + <Field + component={ReviewerInput} + name="user" + reviewerUsers={reviewerUsers} + validate={required} + /> + <Button disabled={!isValid} primary type="submit"> + Invite reviewer + </Button> + </FieldAndButton> </form> ) diff --git a/app/components/component-review/src/components/reviewers/Reviewers.js b/app/components/component-review/src/components/reviewers/Reviewers.js index 2cdf32c8c50f3683e8868ab50414cda853cd712b..b084c6110b6f692deffa1e6a11fe8c683e8cab01 100644 --- a/app/components/component-review/src/components/reviewers/Reviewers.js +++ b/app/components/component-review/src/components/reviewers/Reviewers.js @@ -1,20 +1,31 @@ import React from 'react' import styled from 'styled-components' -import { Link } from '@pubsweet/ui' +import { Action, Button } from '@pubsweet/ui' +import { grid } from '@pubsweet/ui-toolkit' import ReviewerForm from './ReviewerForm' -import { Container, PaddedContent } from '../../../../shared' +import { + Container, + SectionRow, + SectionContent, + SectionHeader, + Title, + Heading, + HeadingWithAction, + StatusBadge, +} from '../../../../shared' // TODO: Make this a proper shared component? import { UserAvatar } from '../../../../component-avatar/src' -const Form = styled.div`` const ReviewersList = styled.div` - display: flex; - flex-wrap: wrap; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(${grid(15)}, 1fr)); + grid-gap: ${grid(2)}; ` +const Reviewer = styled.div`` + const Reviewers = ({ - Reviewer, journal, isValid, loadOptions, @@ -23,11 +34,30 @@ const Reviewers = ({ reviewerUsers, manuscript, handleSubmit, + removeReviewer, teams, + history, }) => ( <Container> - <PaddedContent> - <Form> + <HeadingWithAction> + <Heading>Reviewers</Heading> + <Button + onClick={() => + history.push( + `/journal/versions/${manuscript.id}/decisions/${manuscript.id}`, + ) + } + primary + > + Back to control panel + </Button> + </HeadingWithAction> + <SectionContent> + <SectionHeader> + <Title>Invite reviewers</Title> + </SectionHeader> + + <SectionRow> <ReviewerForm handleSubmit={handleSubmit} isValid={isValid} @@ -35,23 +65,40 @@ const Reviewers = ({ loadOptions={loadOptions} reviewerUsers={reviewerUsers} /> - <Link - to={`/journal/versions/${manuscript.id}/decisions/${manuscript.id}`} - > - Back to Control Panel - </Link> - </Form> - </PaddedContent> - <PaddedContent> - {reviewers && ( - <ReviewersList> - {reviewers.map(reviewer => ( - <UserAvatar key={reviewer.id} user={reviewer.user} /> - // <Reviewer journal={journal} key={reviewer.id} username={reviewer.user.username} /> - ))} - </ReviewersList> - )} - </PaddedContent> + </SectionRow> + </SectionContent> + <SectionContent> + <SectionHeader> + <Title>Reviewer status</Title> + </SectionHeader> + <SectionRow> + {reviewers && ( + <ReviewersList> + {reviewers.map(reviewer => ( + <Reviewer> + <StatusBadge minimal status={reviewer.status} /> + <UserAvatar key={reviewer.id} user={reviewer.user} /> + {reviewer.user.defaultIdentity.name} + <div> + <Action + onClick={() => + removeReviewer({ + variables: { + userId: reviewer.user.id, + manuscriptId: manuscript.id, + }, + }) + } + > + Delete + </Action> + </div> + </Reviewer> + ))} + </ReviewersList> + )} + </SectionRow> + </SectionContent> </Container> ) diff --git a/app/components/component-submit/src/components/Confirm.js b/app/components/component-submit/src/components/Confirm.js index 95413c68ba357ebce6f3ac1702dc4f303ce7ab38..f2ac2d6a6a2fea6ec4f1564a3d1be9dbdcc65225 100644 --- a/app/components/component-submit/src/components/Confirm.js +++ b/app/components/component-submit/src/components/Confirm.js @@ -28,17 +28,29 @@ const createMarkup = encodedHtml => ({ __html: unescape(encodedHtml), }) -const Confirm = ({ toggleConfirming, form, submitSubmission }) => ( +const Confirm = ({ toggleConfirming, form, submit, errors }) => ( <Wrapper> <article> - <Heading1 dangerouslySetInnerHTML={createMarkup(form.popuptitle)} /> - <Paragraph - dangerouslySetInnerHTML={createMarkup(form.popupdescription)} - /> - <Button onClick={submitSubmission} primary type="submit"> - Submit your manuscript - </Button> - <Divider> or </Divider> + {Object.keys(errors).length > 0 ? ( + <> + <Heading1>Errors in your submission</Heading1> + <Paragraph> + There are errors in your submission, please correct the following:{' '} + {JSON.stringify(errors)} + </Paragraph> + </> + ) : ( + <> + <Heading1 dangerouslySetInnerHTML={createMarkup(form.popuptitle)} /> + <Paragraph + dangerouslySetInnerHTML={createMarkup(form.popupdescription)} + /> + <Button onClick={submit} primary type="submit"> + Submit your manuscript + </Button> + <Divider> or </Divider> + </> + )} <PlainButton onClick={toggleConfirming}> get back to your submission </PlainButton> diff --git a/app/components/component-submit/src/components/Confirm.md b/app/components/component-submit/src/components/Confirm.md deleted file mode 100644 index a5773b2ca69e1727b22fa61800b0969d2ed5ae51..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/Confirm.md +++ /dev/null @@ -1,14 +0,0 @@ -A series of confirmation paragraphs that the user must read and agree to before confirming the submission. - -The user can confirm submission using the primary button, or return to the submission using a link. - -```js -const form = { - haspopup: 'true', - id: 'submit', - name: 'Submission information', - popupdescription: faker.lorem.sentences(50), - popuptitle: faker.lorem.words(3), -} -;<Confirm form={form} /> -``` diff --git a/app/components/component-submit/src/components/CurrentVersion.md b/app/components/component-submit/src/components/CurrentVersion.md deleted file mode 100644 index 2dde98ad20be1788b814f5917e4e50725d277746..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/CurrentVersion.md +++ /dev/null @@ -1,62 +0,0 @@ -A form for entering information about the submission. - -```js -const journal = { - id: faker.random.uuid(), -} - -const manuscript = { - id: faker.random.uuid(), - meta: { - title: faker.lorem.sentence(25), - abstract: faker.lorem.sentence(100), - articleType: 'original-research', - declarations: { - openData: 'yes', - openPeerReview: 'no', - preregistered: 'yes', - previouslySubmitted: 'yes', - researchNexus: 'no', - streamlinedReview: 'no', - }, - }, - suggestions: { - reviewers: { - opposed: faker.name.findName(), - }, - }, - reviews: [ - { - comments: { content: 'this needs review' }, - created: 'Thu Oct 11 2018', - open: false, - recommendation: '', - user: { identities: [] }, - }, - ], -} - -const forms = { - children: [ - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.openData', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.openPeerReview', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.previouslySubmitted', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.researchNexus', - }, - ], -} -;<div style={{ position: 'relative', paddingRight: 100 }}> - <CurrentVersion forms={forms} manuscript={manuscript} journal={journal} /> -</div> -``` diff --git a/app/components/component-submit/src/components/Declarations.md b/app/components/component-submit/src/components/Declarations.md deleted file mode 100644 index 8bf81ab7e5ee43e83633583a3cd003888e903e24..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/Declarations.md +++ /dev/null @@ -1,57 +0,0 @@ -A list of questions that must be answered before submission. The questions are -configured via the journal config on the context. - -```js -const forms = { - children: [ - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.openData', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.openPeerReview', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.previouslySubmitted', - }, - { - title: faker.lorem.sentence(5), - name: 'meta.declarations.researchNexus', - }, - ], -} - -const manuscript = { - id: faker.random.uuid(), - meta: { - title: faker.lorem.sentence(25), - abstract: faker.lorem.sentence(100), - articleType: 'original-research', - declarations: { - openData: 'yes', - openPeerReview: 'no', - preregistered: 'yes', - previouslySubmitted: 'yes', - researchNexus: 'no', - streamlinedReview: 'no', - }, - }, - suggestions: { - reviewers: { - opposed: faker.name.findName(), - }, - }, - reviews: [ - { - comments: { content: 'this needs review' }, - created: 'Thu Oct 11 2018', - open: false, - recommendation: '', - user: { identities: [] }, - }, - ], -} -;<Declarations forms={forms} manuscript={manuscript} /> -``` diff --git a/app/components/component-submit/src/components/FormTemplate.js b/app/components/component-submit/src/components/FormTemplate.js index 6baed800ae0a1e2957ab05330ddd6472879cf433..ea58c3046db5facfadd0bdc2882fc78f825434d2 100644 --- a/app/components/component-submit/src/components/FormTemplate.js +++ b/app/components/component-submit/src/components/FormTemplate.js @@ -11,6 +11,19 @@ import AuthorsInput from './AuthorsInput' import Supplementary from './Supplementary' import Confirm from './Confirm' +// TODO: https://github.com/formium/formik/issues/146#issuecomment-474775723 +// const useFocusOnError = ({ fieldRef, name }) => { +// const formik = useFormikContext() +// const prevSubmitCountRef = React.useRef(formik.submitCount) +// const firstErrorKey = Object.keys(formik.errors)[0] +// React.useEffect(() => { +// if (prevSubmitCountRef.current !== formik.submitCount && !formik.isValid) { +// if (fieldRef.current && firstErrorKey === name) fieldRef.current.focus() +// } +// prevSubmitCountRef.current = formik.submitCount +// }, [formik.submitCount, formik.isValid, firstErrorKey]) +// } + // const Wrapper = styled.div` // font-family: ${th('fontInterface')}; // line-height: 1.3; @@ -219,11 +232,12 @@ export default ({ setTouched, values, setFieldValue, - uploadFile, - createFile, + createSupplementaryFile, onChange, onSubmit, submitSubmission, + errors, + validateForm, ...props }) => ( <Container> @@ -247,9 +261,8 @@ export default ({ <Legend dangerouslySetInnerHTML={createMarkup(element.title)} /> {element.component === 'SupplementaryFiles' && ( <Supplementary - createFile={createFile} + createSupplementaryFile={createSupplementaryFile} onChange={onChange} - uploadFile={uploadFile} /> )} {element.component === 'AuthorsInput' && ( @@ -322,7 +335,11 @@ export default ({ {values.status !== 'submitted' && form.haspopup === 'true' && ( <div> - <Button onClick={toggleConfirming} primary type="button"> + <Button + onClick={() => validateForm() && toggleConfirming()} + primary + type="button" + > Submit your research object </Button> </div> @@ -330,8 +347,9 @@ export default ({ {confirming && ( <ModalWrapper> <Confirm + errors={errors} form={form} - submitSubmission={handleSubmit} + submit={handleSubmit} toggleConfirming={toggleConfirming} /> </ModalWrapper> diff --git a/app/components/component-submit/src/components/MetadataFields.md b/app/components/component-submit/src/components/MetadataFields.md deleted file mode 100644 index 30ec5fb3430ca2caa016fc367aaecc246f9a8c4f..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/MetadataFields.md +++ /dev/null @@ -1,13 +0,0 @@ -A form for entering the submission's metadata. - -```js -const manuscript = { - meta: { - title: faker.lorem.sentence(25), - abstract: faker.lorem.sentence(50), - articleType: 'original-research', - keywords: 'test, test1', - }, -} -;<MetadataFields manuscript={manuscript} /> -``` diff --git a/app/components/component-submit/src/components/NewSubmissionPage.jsx b/app/components/component-submit/src/components/NewSubmissionPage.jsx index 2ce2b53f61730cc79c8d318ae341cf6313c0cdce..922ad7fd029d2d0aa270b17c9c3114682e865b07 100644 --- a/app/components/component-submit/src/components/NewSubmissionPage.jsx +++ b/app/components/component-submit/src/components/NewSubmissionPage.jsx @@ -1,13 +1,10 @@ import React from 'react' -import { useQuery, useMutation, ApolloConsumer } from '@apollo/client' -// import Authorize from 'pubsweet-client/src/helpers/Authorize' - +import { ApolloConsumer } from '@apollo/client' import config from 'config' -import { Container, Content, Section, Heading, UploadContainer, PageHeading } from '../style' +import { Container, Content, UploadContainer, PageHeading } from '../style' import UploadManuscript from './UploadManuscript' -import { Spinner } from '../../../shared' -import { Action } from '@pubsweet/ui' import useCurrentUser from '../../../../hooks/useCurrentUser' + const { acceptUploadFiles } = config['pubsweet-component-xpub-dashboard'] || {} const acceptFiles = @@ -15,7 +12,6 @@ const acceptFiles = ? acceptUploadFiles.join() : 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' - const Dashboard = props => { const currentUser = useCurrentUser() @@ -23,19 +19,18 @@ const Dashboard = props => { <Container> <PageHeading level={1}>New submission</PageHeading> <Content> - <UploadContainer> - <ApolloConsumer> - {client => ( - <UploadManuscript - acceptFiles={acceptFiles} - client={client} - currentUser={currentUser} - history={props.history} - /> - )} - </ApolloConsumer> - </UploadContainer> - + <UploadContainer> + <ApolloConsumer> + {client => ( + <UploadManuscript + acceptFiles={acceptFiles} + client={client} + currentUser={currentUser} + history={props.history} + /> + )} + </ApolloConsumer> + </UploadContainer> </Content> </Container> ) diff --git a/app/components/component-submit/src/components/Submit.js b/app/components/component-submit/src/components/Submit.js index 79f7cdf9d1e12a7a7b3768caa9c3591f03f550e7..d1390b346d0e7734b51c183df429cdc3f1008c37 100644 --- a/app/components/component-submit/src/components/Submit.js +++ b/app/components/component-submit/src/components/Submit.js @@ -24,7 +24,7 @@ const SubmittedVersionColumns = props => ( </Container> ) -const Submit = ({ journal, manuscript, forms, ...formProps }) => { +const Submit = ({ manuscript, forms, ...formProps }) => { const decisionSections = [] const manuscriptVersions = manuscript.manuscriptVersions || [] manuscriptVersions.forEach(versionElem => { @@ -32,11 +32,7 @@ const Submit = ({ journal, manuscript, forms, ...formProps }) => { const label = submittedMoment.format('YYYY-MM-DD') decisionSections.push({ content: ( - <SubmittedVersionColumns - forms={forms} - journal={journal} - manuscript={versionElem} - /> + <SubmittedVersionColumns forms={forms} manuscript={versionElem} /> ), key: versionElem.id, label, @@ -46,12 +42,7 @@ const Submit = ({ journal, manuscript, forms, ...formProps }) => { decisionSections.push({ content: ( <Content> - <FormTemplate - {...formProps} - form={forms} - journal={journal} - manuscript={manuscript} - /> + <FormTemplate {...formProps} form={forms} manuscript={manuscript} /> </Content> ), key: manuscript.id, diff --git a/app/components/component-submit/src/components/SubmitPage.js b/app/components/component-submit/src/components/SubmitPage.js index 6bd3e8adab5575cc305e712363ed75d521aafcf0..5c6ce3ed5bfc80c4b458a073b6829b00b553a2a3 100644 --- a/app/components/component-submit/src/components/SubmitPage.js +++ b/app/components/component-submit/src/components/SubmitPage.js @@ -1,16 +1,10 @@ -import { debounce, cloneDeep, isEmpty, set } from 'lodash' -import { compose, withProps, withState, withHandlers } from 'recompose' -import { graphql } from '@apollo/client/react/hoc' -import gql from 'graphql-tag' -import { withFormik } from 'formik' -import { withLoader } from 'pubsweet-client' +import React, { useState } from 'react' +import { debounce, cloneDeep, set } from 'lodash' +// import { compose, withProps, withState, withHandlers } from 'recompose' +import { gql, useQuery, useMutation } from '@apollo/client' +import { Formik } from 'formik' import Submit from './Submit' - -const nullToEmpty = obj => - JSON.parse(JSON.stringify(obj, (k, v) => (v === null ? '' : v))) - -const emptyToUndefined = obj => - JSON.parse(JSON.stringify(obj, (k, v) => (v === '' ? undefined : v))) +import { Spinner } from '../../../shared' const fragmentFields = ` id @@ -26,6 +20,7 @@ const fragmentFields = ` url } reviews { + id open recommendation created @@ -121,17 +116,17 @@ const updateMutation = gql` } ` -const uploadSuplementaryFilesMutation = gql` - mutation($file: Upload!) { - upload(file: $file) { - url - } - } -` +// const uploadSuplementaryFilesMutation = gql` +// mutation($file: Upload!) { +// upload(file: $file) { +// url +// } +// } +// ` const createFileMutation = gql` - mutation($file: Upload!) { - createFile(file: $file) { + mutation($file: Upload!, $meta: FileMetaInput) { + createFile(file: $file, meta: $meta) { id created label @@ -144,116 +139,102 @@ const createFileMutation = gql` } ` -export default compose( - graphql(query, { - options: ({ match }) => ({ +const SubmitPage = ({ match, history, ...props }) => { + const [confirming, setConfirming] = useState(false) + + const toggleConfirming = () => { + setConfirming(confirming => !confirming) + } + + const { data, loading, error } = useQuery(query, { + 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: { - id: match.params.version, - form: 'submit', + file, + meta, }, - }), - props: ({ data }) => ({ data: nullToEmpty(data) }), - }), - graphql(createFileMutation, { - props: ({ mutate, ownProps }) => ({ - createFile: value => { - const file = { - url: value.url, - filename: value.filename, - mimeType: value.mimeType, - size: value.size, - fileType: 'supplementary', - object: 'Manuscript', - objectId: ownProps.match.params.version, - } + }) + } - mutate({ - variables: { - file, - }, - }) + const [update] = useMutation(updateMutation) + + if (loading) return <Spinner /> + if (error) return error + + const manuscript = data?.manuscript + const getFile = data?.getFile + + const updateManuscript = input => + update({ + variables: { + id: match.params.version, + input: JSON.stringify(input), }, - }), - }), - graphql(uploadSuplementaryFilesMutation, { - props: ({ mutate, ownProps }) => ({ - uploadFile: file => - mutate({ - variables: { - file, - }, - }), - }), - }), - graphql(updateMutation, { - props: ({ mutate, ownProps }) => { - const debouncers = {} - const onChange = (value, path) => { - const input = {} - set(input, path, value) - debouncers[path] = debouncers[path] || debounce(updateManuscript, 300) - return debouncers[path](input) - } + }) - const updateManuscript = input => - mutate({ - variables: { - id: ownProps.match.params.version, - input: JSON.stringify(emptyToUndefined(input)), - }, - }) + const debouncers = {} - return { - onChange, - } - }, - }), - graphql(updateMutation, { - props: ({ mutate, ownProps }) => ({ - onSubmit: (manuscript, { history }) => { - const updateManuscript = { - status: 'submitted', - } + const handleChange = (value, path) => { + const input = {} + set(input, path, value) + debouncers[path] = debouncers[path] || debounce(updateManuscript, 300) + return debouncers[path](input) + } - mutate({ - variables: { - id: ownProps.match.params.version, - input: JSON.stringify(updateManuscript), - }, - }).then(() => { - history.push('/journal/dashboard') - }) + const onSubmit = async manuscript => { + const updateManuscript = { + status: 'submitted', + } + + await update({ + variables: { + id: match.params.version, + input: JSON.stringify(updateManuscript), }, - }), - }), - withLoader(), - withProps(({ getFile, manuscript, match: { params: { journal } } }) => ({ - journal: { id: journal }, - forms: cloneDeep(getFile), - manuscript, - submitSubmission: ({ validateForm, setSubmitting, handleSubmit }) => - validateForm().then(props => - isEmpty(props) ? setSubmitting(false) : handleSubmit(), - ), - })), - withFormik({ - initialValues: {}, - mapPropsToValues: ({ manuscript }) => - Object.assign({}, manuscript, { + }) + history.push('/journal/dashboard') + } + + return ( + <Formik + displayName="submit" + handleChange={handleChange} + initialValues={Object.assign({}, manuscript, { submission: JSON.parse(manuscript.submission), - }), - displayName: 'submit', - handleSubmit: ( - props, - { validateForm, setSubmitting, props: { onSubmit, history } }, - ) => - validateForm().then(props => - isEmpty(props) ? onSubmit(props, { history }) : setSubmitting(false), - ), - }), - withState('confirming', 'setConfirming', false), - withHandlers({ - toggleConfirming: ({ validateForm, setConfirming, handleSubmit }) => () => - setConfirming(confirming => !confirming), - }), -)(Submit) + })} + onSubmit={async (values, { validateForm, setSubmitting, ...other }) => { + // TODO: Change this to a more Formik idiomatic form + const isValid = Object.keys(await validateForm()).length === 0 + return isValid ? onSubmit(values) : setSubmitting(false) + }} + > + {props => ( + <Submit + confirming={confirming} + createSupplementaryFile={createSupplementaryFile} + forms={cloneDeep(getFile)} + manuscript={manuscript} + onChange={handleChange} + toggleConfirming={toggleConfirming} + {...props} + /> + )} + </Formik> + ) +} + +export default SubmitPage diff --git a/app/components/component-submit/src/components/Suggestions.md b/app/components/component-submit/src/components/Suggestions.md deleted file mode 100644 index ee1f366c9dd8c798bd9f341d86e6f515d9c6428d..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/Suggestions.md +++ /dev/null @@ -1,12 +0,0 @@ -A list of questions that must be answered before submission. - -```js -const manuscript = { - suggestions: { - reviewers: { - opposed: faker.name.findName(), - }, - }, -} -;<Suggestions manuscript={manuscript} /> -``` diff --git a/app/components/component-submit/src/components/Supplementary.js b/app/components/component-submit/src/components/Supplementary.js index 2580f3e0353de173b074856e9b47d4364f72b351..0432c9488da0896b8e7e21522fc7629516d82cc2 100644 --- a/app/components/component-submit/src/components/Supplementary.js +++ b/app/components/component-submit/src/components/Supplementary.js @@ -3,7 +3,7 @@ import { cloneDeep } from 'lodash' import { FieldArray } from 'formik' import { Flexbox, UploadButton, UploadingFile } from '@pubsweet/ui' -const renderFilesUpload = (onChange, uploadFile, createFile) => ({ +const renderFilesUpload = (onChange, createSupplementaryFile) => ({ form: { values, setFieldValue }, push, insert, @@ -21,15 +21,7 @@ const renderFilesUpload = (onChange, uploadFile, createFile) => ({ }) setFieldValue('files', fileArray.concat(values.files)) Array.from(event.target.files).forEach(file => { - uploadFile(file).then(({ data }) => { - const newFile = { - url: data.upload.url, - filename: file.name, - mimeType: file.type, - size: file.size, - } - createFile(newFile) - }) + createSupplementaryFile(file) }) }} /> @@ -44,10 +36,10 @@ const renderFilesUpload = (onChange, uploadFile, createFile) => ({ </> ) -const Supplementary = ({ onChange, uploadFile, createFile }) => ( +const Supplementary = ({ onChange, createSupplementaryFile }) => ( <FieldArray name="files" - render={renderFilesUpload(onChange, uploadFile, createFile)} + render={renderFilesUpload(onChange, createSupplementaryFile)} /> ) diff --git a/app/components/component-submit/src/components/SupplementaryFiles.md b/app/components/component-submit/src/components/SupplementaryFiles.md deleted file mode 100644 index 46399c40d007bb451511d6d4058816e54ce18ab5..0000000000000000000000000000000000000000 --- a/app/components/component-submit/src/components/SupplementaryFiles.md +++ /dev/null @@ -1,20 +0,0 @@ -A form for entering the submission's supplementary material. - -```js -const file = () => ({ - file: { - url: faker.internet.url(), - name: faker.system.commonFileName(), - }, - filename: faker.system.commonFileName(), - type: 'supplementary', -}) - -const manuscript = { - files: [file(), file(), file()], -} -;<SupplementaryFiles - manuscript={manuscript} - onChange={values => console.log(values)} -/> -``` diff --git a/app/components/component-submit/src/upload.js b/app/components/component-submit/src/upload.js index 006a495cdaac5d1b18806b30bc339a414b42605f..2f06bf098e004f8de51fd787423bd4a6da631185 100644 --- a/app/components/component-submit/src/upload.js +++ b/app/components/component-submit/src/upload.js @@ -1,10 +1,6 @@ import config from 'config' import request from 'pubsweet-client/src/helpers/api' import gql from 'graphql-tag' -// TOOD: These queries come from the dashboard component, -// making this a tricky dependency. Should we extract them into -// a shared component? -import queries from '../../component-dashboard/src/graphql/queries' const generateTitle = name => name @@ -156,17 +152,24 @@ const createManuscriptPromise = ( return client.mutate({ mutation: createManuscriptMutation, variables: { input: manuscript }, - update: (proxy, { data: { createManuscript } }) => { - let data = proxy.readQuery({ query: queries.dashboard }) - data.manuscripts.push(createManuscript) - proxy.writeQuery({ query: queries.dashboard, data }) - - data = proxy.readQuery({ - query: queries.getUser, - variables: { id: currentUser.id }, + update: (cache, { data: { createManuscript } }) => { + cache.modify({ + fields: { + manuscripts(existingManuscriptRefs = []) { + // Get the reference for the cache entry generated by useMutation + const newManuscriptRef = cache.writeFragment({ + data: createManuscript, + fragment: gql` + fragment NewManuscript on Manuscript { + id + } + `, + }) + + return [...existingManuscriptRefs, newManuscriptRef] + }, + }, }) - data.user.teams.push(createManuscript.teams[0]) - proxy.writeQuery({ query: queries.getUser, data }) }, }) } diff --git a/app/components/component-teams-manager/src/components/Team.jsx b/app/components/component-teams-manager/src/components/Team.jsx index 9a04a9065b620b9720f16010d3fc84019356310d..66535822dfae39ab2d1e612b7141ca26b1721575 100644 --- a/app/components/component-teams-manager/src/components/Team.jsx +++ b/app/components/component-teams-manager/src/components/Team.jsx @@ -14,7 +14,8 @@ const Team = ({ team, number, userOptions, deleteTeam, updateTeam }) => ( {team.name} {team.role} </TeamTableCell> <TeamTableCell> - {team.object && team.object.objectType} {team.object && team.object.objectId} + {team.object && team.object.objectType}{' '} + {team.object && team.object.objectId} </TeamTableCell> <TeamTableCell width={40}> <StyledMenu diff --git a/app/components/component-users-manager/src/UsersManager.jsx b/app/components/component-users-manager/src/UsersManager.jsx index 5a09925c7978a4b2f0839064440df7e8c0540a9f..72869f557d834fe92c44181e9a5138377bf501ef 100644 --- a/app/components/component-users-manager/src/UsersManager.jsx +++ b/app/components/component-users-manager/src/UsersManager.jsx @@ -44,7 +44,6 @@ const GET_USERS = gql` } ` - const UsersManager = () => { const SortHeader = ({ thisSortName, children }) => { const changeSort = () => { diff --git a/app/components/shared/Badge.js b/app/components/shared/Badge.js index f7f1a2e873fa848a66930d08347af8dc5dbe08f4..29e9cc2a5aa02a7180571370a45ca0a09dd78ff6 100644 --- a/app/components/shared/Badge.js +++ b/app/components/shared/Badge.js @@ -59,6 +59,8 @@ const label = status => { rejected: 'Rejected', submitted: 'Submitted', revise: 'Revising', + invited: 'Invited', // reviewer status + completed: 'Completed', // reviewer status } return labels[status] || `Unknown (${status})` } diff --git a/app/components/shared/General.js b/app/components/shared/General.js index f0e4551f704bd66c0477c4f42440388f3a786604..dc6ca58eda32534b3085339d92de8c3087f12d11 100644 --- a/app/components/shared/General.js +++ b/app/components/shared/General.js @@ -59,3 +59,22 @@ export const SectionAction = styled.div` grid-column: 3; justify-self: end; ` + +const Page = styled.div` + padding: ${grid(2)}; +` + +const Heading = styled.div` + color: ${th('colorPrimary')}; + font-family: ${th('fontReading')}; + font-size: ${th('fontSizeHeading3')}; + line-height: ${th('lineHeightHeading3')}; +` + +export { Page, Heading } + +export const HeadingWithAction = styled.div` + display: grid; + grid-template-columns: 1fr auto; + align-items: center; +` diff --git a/app/components/shared/Pagination.jsx b/app/components/shared/Pagination.jsx index 8a395f37f83c0eb73d95ea7c36680c8b2484b662..e5b5aa3b831b6e3369dafcc130d4273fc54c28de 100644 --- a/app/components/shared/Pagination.jsx +++ b/app/components/shared/Pagination.jsx @@ -144,4 +144,3 @@ export const Pagination = ({ setPage, limit, page, totalCount }) => { </PaginationContainer> ) } - diff --git a/app/components/shared/Select.js b/app/components/shared/Select.js new file mode 100644 index 0000000000000000000000000000000000000000..a28127cd67ccd28d927b969f02d2d4cc51d97fb8 --- /dev/null +++ b/app/components/shared/Select.js @@ -0,0 +1,42 @@ +import React, { useContext } from 'react' +import ReactSelect from 'react-select' +import { ThemeContext } from 'styled-components' + +const styles = th => ({ + menu: (provided, state) => ({ + ...provided, + borderRadius: th.borderRadius, + }), + + control: (provided, state) => ({ + ...provided, + border: state.isFocused + ? `1px solid ${th.colorPrimary}` + : `1px solid ${th.colorBorder}`, + boxShadow: state.isFocused ? `0 0 0 1px ${th.colorPrimary}` : 'none', + borderRadius: th.borderRadius, + '&:hover': { + boxShadow: `0 0 0 1px ${th.colorPrimary}`, + }, + minHeight: `calc(${th.gridUnit} * 5)`, + }), + + singleValue: (provided, state) => { + const opacity = state.isDisabled ? 0.5 : 1 + const transition = 'opacity 300ms' + + return { ...provided, opacity, transition } + }, + + option: (provided, state) => ({ + ...provided, + backgroundColor: + state.isFocused || state.isSelected ? th.colorFurniture : 'white', + color: th.colorText, + }), +}) + +export const Select = props => { + const theme = useContext(ThemeContext) + return <ReactSelect {...props} styles={styles(theme)} /> +} diff --git a/app/components/shared/index.js b/app/components/shared/index.js index 7ac615184caed50bd15b8b63e77bb5142ce2515f..9c03d0839662980df9d89035eb141a227f8a089b 100644 --- a/app/components/shared/index.js +++ b/app/components/shared/index.js @@ -7,3 +7,4 @@ export * from './UserCombo' export * from './Table' export * from './General' export * from './Badge' +export * from './Select' diff --git a/app/fragmentTypes.json b/app/fragmentTypes.json deleted file mode 100644 index 571ad559ceacb8cc27ca7155a64e35a74a922336..0000000000000000000000000000000000000000 --- a/app/fragmentTypes.json +++ /dev/null @@ -1 +0,0 @@ -{"__schema":{"types":[{"kind":"INTERFACE","name":"Identity","possibleTypes":[{"name":"LocalIdentity"},{"name":"ExternalIdentity"}]},{"kind":"INTERFACE","name":"Object","possibleTypes":[{"name":"Manuscript"},{"name":"ManuscriptVersion"},{"name":"File"},{"name":"Review"},{"name":"Note"}]}]}} \ No newline at end of file diff --git a/app/index.html b/app/index.html index 4fe0d77d5a1c54430fd7c7e3bb6f7442a61afa72..6868ec90f5d475288e4dd90a176dc43155677c8d 100644 --- a/app/index.html +++ b/app/index.html @@ -2,7 +2,7 @@ <html> <head> <meta charset="utf-8"> - <title>SimpleJ</title> + <title>Kotahi</title> </head> <body> <div id="root"></div> diff --git a/app/queries/index.js b/app/queries/index.js index 17d6b3ba206f39b9addd2edd475969fc85f5c51a..6f86f24089190cc2f054069b6b881f52872b5d02 100644 --- a/app/queries/index.js +++ b/app/queries/index.js @@ -8,15 +8,12 @@ export const GET_CURRENT_USER = gql` username admin defaultIdentity { + identifier + email + type aff + id name - type - ... on ExternalIdentity { - identifier - } - ... on LocalIdentity { - email - } } online _currentRoles { diff --git a/app/shared/prettyRoleText.js b/app/shared/prettyRoleText.js new file mode 100644 index 0000000000000000000000000000000000000000..50c243c0dedd05e898d73a1391b358e71a4c03ad --- /dev/null +++ b/app/shared/prettyRoleText.js @@ -0,0 +1,8 @@ +const config = require('config') + +module.exports = (roles = []) => { + const prettyRoles = config.journal.roles + + const roleText = roles.map(r => prettyRoles[r] || r).join(', ') + return roleText +} diff --git a/config/journal/roles.js b/config/journal/roles.js index 95129e0a1ec5d4e512cf548d971ea7f470f04c5b..066d9dad6cbadca7cf8d0b472f083d550948bc1c 100644 --- a/config/journal/roles.js +++ b/config/journal/roles.js @@ -3,4 +3,5 @@ module.exports = { handlingEditor: 'Handling Editor', managingEditor: 'Managing Editor', seniorEditor: 'Senior Editor', + reviewer: 'Reviewer', } diff --git a/cypress/dumps/3reviewscompleted.sql b/cypress/dumps/3reviewscompleted.sql deleted file mode 100644 index d2ee088104791b09b11e68681a881c3e5526c9fe..0000000000000000000000000000000000000000 --- a/cypress/dumps/3reviewscompleted.sql +++ /dev/null @@ -1,468 +0,0 @@ --- --- 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'; - - -SET default_tablespace = ''; - -SET default_with_oids = false; - --- --- 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 character varying(255), - email character varying(255), - aff character varying(255) -); - - -ALTER TABLE public.aliases 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, - object text, - object_id uuid, - label text, - file_type text, - filename text, - url text, - mime_type text, - size integer, - type text NOT NULL -); - - -ALTER TABLE public.files OWNER TO test; - --- --- Name: journals; Type: TABLE; Schema: public; Owner: test --- - -CREATE TABLE public.journals ( - id uuid NOT NULL, - created timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - updated timestamp with time zone, - title text, - meta jsonb, - type text NOT NULL -); - - -ALTER TABLE public.journals 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, - status text, - decision text, - authors jsonb, - suggestions jsonb, - meta jsonb, - type text NOT NULL -); - - -ALTER TABLE public.manuscripts 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: 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, - comments jsonb, - 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, - owners jsonb, - global boolean, - type text NOT NULL, - object_id uuid, - object_type character varying(255) -); - - -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, - fragments jsonb, - collections jsonb, - teams jsonb, - password_reset_token text, - password_reset_timestamp timestamp with time zone, - type text NOT NULL -); - - -ALTER TABLE public.users OWNER TO test; - --- --- Data for Name: aliases; Type: TABLE DATA; Schema: public; Owner: test --- - - - --- --- Data for Name: entities; Type: TABLE DATA; Schema: public; Owner: test --- - - - --- --- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: test --- - -INSERT INTO public.files (id, created, updated, object, object_id, label, file_type, filename, url, mime_type, size, type) VALUES ('60972cc9-b5b9-42b4-a80f-67616cfa6ac7', '2020-02-28 19:39:59.454+01', '2020-02-28 19:39:59.454+01', 'Manuscript', 'bd429b57-6fc9-4454-a42c-c1f63cfa263c', NULL, 'manuscript', 'test-pdf.pdf', '/0b5e0a112164206f396e61beb28a913b.pdf', 'application/pdf', 106798, 'file'); - - --- --- Data for Name: journals; Type: TABLE DATA; Schema: public; Owner: test --- - -INSERT INTO public.journals (id, created, updated, title, meta, type) VALUES ('4fe21415-b60c-442e-8748-872f35c2266f', '2020-02-28 19:39:55.414+01', '2020-02-28 19:39:55.414+01', 'My Journal', NULL, 'journal'); - - --- --- Data for Name: manuscripts; Type: TABLE DATA; Schema: public; Owner: test --- - -INSERT INTO public.manuscripts (id, created, updated, parent_id, status, decision, authors, suggestions, meta, type) VALUES ('bd429b57-6fc9-4454-a42c-c1f63cfa263c', '2020-02-28 19:39:59.308+01', '2020-02-28 19:40:15.15+01', NULL, 'submitted', NULL, NULL, '{"editors": {"opposed": "Gina Ode", "suggested": "John Ode"}, "reviewers": {"opposed": "James Doe", "suggested": "Jane Do"}}', '{"notes": [{"content": "This work was supported by the Trust [grant numbers 393,295]; the Natural Environment Research Council [grant number 49493].", "notesType": "fundingAcknowledgement"}, {"content": "", "notesType": "specialInstructions", "__typename": "Note"}], "title": "A Manuscript For The Ages", "keywords": "quantum, machines, nature", "articleType": "original-research", "declarations": {"openData": "yes", "preregistered": "yes", "researchNexus": "no", "openPeerReview": "yes", "streamlinedReview": "no", "previouslySubmitted": "no"}, "articleSections": ["cognitive-psychology"]}', 'Manuscript'); - - --- --- Data for Name: migrations; Type: TABLE DATA; Schema: public; Owner: test --- - -INSERT INTO public.migrations (id, run_at) VALUES ('1524494862-entities.sql', '2020-02-28 19:39:52.52103+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-files.sql', '2020-02-28 19:39:52.533068+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-journals.sql', '2020-02-28 19:39:52.582771+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-manuscript.sql', '2020-02-28 19:39:52.628646+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1537450834-review.sql', '2020-02-28 19:39:52.682145+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1542276313-initial-user-migration.sql', '2020-02-28 19:39:52.700352+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1542801241-initial-team-migration.sql', '2020-02-28 19:39:52.713315+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1547596236-initial-team-member-migration.js', '2020-02-28 19:39:52.732321+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1548205275-move-members.js', '2020-02-28 19:39:52.743401+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1548205276-simplify-object.js', '2020-02-28 19:39:52.765508+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1548328420-add-alias-migration.js', '2020-02-28 19:39:52.783812+01'); -INSERT INTO public.migrations (id, run_at) VALUES ('1560771823-add-unique-constraints-to-users.sql', '2020-02-28 19:39:52.794199+01'); - - --- --- Data for Name: reviews; Type: TABLE DATA; Schema: public; Owner: test --- - -INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, comments, user_id, manuscript_id, type) VALUES ('622e74ac-f08b-420f-b311-076e69b03c00', '2020-02-28 19:40:33.481+01', '2020-02-28 19:40:40.542+01', 'accepted', false, '[{"type": "note", "content": "Great research into CC bases in the ky289 variant are mutated to TC which results in the truncation of the SAD-1."}, {"type": "confidential", "content": "Not too bad."}]', 'e910b3d3-273d-4492-b68b-b33a5e4cb58d', 'bd429b57-6fc9-4454-a42c-c1f63cfa263c', 'Review'); -INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, comments, user_id, manuscript_id, type) VALUES ('5c8b933e-6410-46f4-8949-85ad3b1fc061', '2020-02-28 19:40:44.503+01', '2020-02-28 19:40:50.2+01', 'revise', false, '[{"type": "note", "content": "Mediocre analysis of Iron-Sulfur ClUster assembly enzyme homolog."}, {"type": "confidential", "content": "It is so so."}]', '1e28bd99-497d-4496-8e1a-169693995b18', 'bd429b57-6fc9-4454-a42c-c1f63cfa263c', 'Review'); -INSERT INTO public.reviews (id, created, updated, recommendation, is_decision, comments, user_id, manuscript_id, type) VALUES ('88d41d50-b647-4654-a8e0-ab5bb6934124', '2020-02-28 19:40:54.824+01', '2020-02-28 19:41:01.495+01', 'rejected', false, '[{"type": "note", "content": "mTOR-Is positively influence the occurrence and course of certain tumors after solid organ transplantation."}, {"type": "confidential", "content": "It is not good."}]', '60adcc92-64a0-4d87-97ef-a31ccf30517a', 'bd429b57-6fc9-4454-a42c-c1f63cfa263c', '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 ('a5ff0f55-6363-4c0f-a033-27be3b55ff37', '2020-02-28 19:39:59.499+01', '2020-02-28 19:39:59.499+01', NULL, 'ebb4cc52-04ed-4390-ad70-be8f438e6012', 'bbe263a7-3cc5-4b3a-a6ce-d2cc70425406', NULL); -INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('2ef219dd-66b4-45b4-b525-89a6b64e9eeb', '2020-02-28 19:40:21.509+01', '2020-02-28 19:40:21.509+01', NULL, '5848e7e9-112e-41cc-8f93-460560a740aa', '1c162384-ef4c-47fc-a89e-d7a03c13cec8', NULL); -INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('9a362034-2f53-4162-a5e4-d3fa6896ff5e', '2020-02-28 19:40:21.949+01', '2020-02-28 19:40:21.949+01', NULL, '84ef271d-a10c-4d3c-9c44-0568f126e161', '53c1ddca-2770-417d-991d-f386d2d06e57', NULL); -INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('84235add-df75-48cf-8a39-ef18faea1536', '2020-02-28 19:41:01.778+01', '2020-02-28 19:41:01.778+01', 'completed', '9c00c3e3-9d53-48d8-805d-8ffa77f12d82', '1e28bd99-497d-4496-8e1a-169693995b18', NULL); -INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('51b06270-cafd-46d9-af06-9ee5a703c41d', '2020-02-28 19:41:01.778+01', '2020-02-28 19:41:01.778+01', 'completed', '9c00c3e3-9d53-48d8-805d-8ffa77f12d82', '60adcc92-64a0-4d87-97ef-a31ccf30517a', NULL); -INSERT INTO public.team_members (id, created, updated, status, team_id, user_id, alias_id) VALUES ('6da43ddf-b03c-4c1e-b304-517d563aa3f9', '2020-02-28 19:41:01.778+01', '2020-02-28 19:41:01.778+01', 'completed', '9c00c3e3-9d53-48d8-805d-8ffa77f12d82', 'e910b3d3-273d-4492-b68b-b33a5e4cb58d', 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 ('ebb4cc52-04ed-4390-ad70-be8f438e6012', '2020-02-28 19:39:59.452+01', '2020-02-28 19:39:59.452+01', 'Author', 'author', NULL, NULL, 'team', 'bd429b57-6fc9-4454-a42c-c1f63cfa263c', 'Manuscript'); -INSERT INTO public.teams (id, created, updated, name, role, owners, global, type, object_id, object_type) VALUES ('5848e7e9-112e-41cc-8f93-460560a740aa', '2020-02-28 19:40:21.505+01', '2020-02-28 19:40:21.505+01', 'Senior Editor', 'seniorEditor', '["2d88ee35-1fa1-4d49-8f87-0b32bb786c9e"]', NULL, 'team', 'bd429b57-6fc9-4454-a42c-c1f63cfa263c', 'Manuscript'); -INSERT INTO public.teams (id, created, updated, name, role, owners, global, type, object_id, object_type) VALUES ('84ef271d-a10c-4d3c-9c44-0568f126e161', '2020-02-28 19:40:21.947+01', '2020-02-28 19:40:21.947+01', 'Handling Editor', 'handlingEditor', '["2d88ee35-1fa1-4d49-8f87-0b32bb786c9e"]', NULL, 'team', 'bd429b57-6fc9-4454-a42c-c1f63cfa263c', 'Manuscript'); -INSERT INTO public.teams (id, created, updated, name, role, owners, global, type, object_id, object_type) VALUES ('9c00c3e3-9d53-48d8-805d-8ffa77f12d82', '2020-02-28 19:40:27.284+01', '2020-02-28 19:40:54.755+01', 'Reviewer Editor', 'reviewerEditor', '["53c1ddca-2770-417d-991d-f386d2d06e57"]', NULL, 'team', 'bd429b57-6fc9-4454-a42c-c1f63cfa263c', 'Manuscript'); - - --- --- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: test --- - -INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, fragments, collections, teams, password_reset_token, password_reset_timestamp, type) VALUES ('2d88ee35-1fa1-4d49-8f87-0b32bb786c9e', '2020-02-28 19:39:53.113+01', '2020-02-28 19:39:53.113+01', true, 'admin@example.com', 'admin', '$2b$12$1qAk620zjS3m0H64vepNc.YZLMHtFyFfoIFLH721MltGTovng9U/m', '[]', '[]', NULL, NULL, NULL, 'user'); -INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, fragments, collections, teams, password_reset_token, password_reset_timestamp, type) VALUES ('1c162384-ef4c-47fc-a89e-d7a03c13cec8', '2020-02-28 19:39:53.855+01', '2020-02-28 19:39:53.855+01', NULL, 'simone@example.com', 'seditor', '$2b$12$jeVSGVlpc3W0PkcS5HneBezNtqdFMUuJSiOGXXYfr0Xtjm2laKM8y', '[]', '[]', NULL, NULL, NULL, 'user'); -INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, fragments, collections, teams, password_reset_token, password_reset_timestamp, type) VALUES ('53c1ddca-2770-417d-991d-f386d2d06e57', '2020-02-28 19:39:54.24+01', '2020-02-28 19:39:54.24+01', NULL, 'hector@example.com', 'heditor', '$2b$12$p.p4JZBES6JXCD2lqO9D9ORqiZj64ECCoRBpvzHsLPLjYHcnCM/J6', '[]', '[]', NULL, NULL, NULL, 'user'); -INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, fragments, collections, teams, password_reset_token, password_reset_timestamp, type) VALUES ('bbe263a7-3cc5-4b3a-a6ce-d2cc70425406', '2020-02-28 19:39:53.504+01', '2020-02-28 19:39:59.54+01', NULL, 'john@example.com', 'author', '$2b$12$Bjr9z5v7nPTSDzHG6YtVQ.9M8QBsbiRbkSAeFiwEPZrTqpkxroxEi', '[]', '[]', NULL, NULL, NULL, 'user'); -INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, fragments, collections, teams, password_reset_token, password_reset_timestamp, type) VALUES ('1e28bd99-497d-4496-8e1a-169693995b18', '2020-02-28 19:39:55.011+01', '2020-02-28 19:41:01.782+01', NULL, 'robert@example.com', 'reviewer2', '$2b$12$E20/golpY0zs.NCSdziUHOfj7GSz7C.CYSDwJh4lX3hboRAo56wtC', '[]', '[]', NULL, NULL, NULL, 'user'); -INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, fragments, collections, teams, password_reset_token, password_reset_timestamp, type) VALUES ('60adcc92-64a0-4d87-97ef-a31ccf30517a', '2020-02-28 19:39:55.367+01', '2020-02-28 19:41:01.782+01', NULL, 'remionne@example.com', 'reviewer3', '$2b$12$7JVUD5Rhw5bqXwXYbjgFIuuHLcLaaPafwqMOQCab32x6VCaMnwNqm', '[]', '[]', NULL, NULL, NULL, 'user'); -INSERT INTO public.users (id, created, updated, admin, email, username, password_hash, fragments, collections, teams, password_reset_token, password_reset_timestamp, type) VALUES ('e910b3d3-273d-4492-b68b-b33a5e4cb58d', '2020-02-28 19:39:54.618+01', '2020-02-28 19:41:01.782+01', NULL, 'regina@example.com', 'reviewer1', '$2b$12$4zYpy2jtXufGR3ib/Sh0mOTZHTeuIu1L9PqZKn7pgMs2zq7dXsDuq', '[]', '[]', NULL, NULL, NULL, 'user'); - - --- --- Name: aliases aliases_pkey; Type: CONSTRAINT; Schema: public; Owner: test --- - -ALTER TABLE ONLY public.aliases - ADD CONSTRAINT aliases_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: journals journals_pkey; Type: CONSTRAINT; Schema: public; Owner: test --- - -ALTER TABLE ONLY public.journals - ADD CONSTRAINT journals_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: migrations migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: test --- - -ALTER TABLE ONLY public.migrations - ADD CONSTRAINT migrations_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: team_members_team_id_user_id_index; 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); - - --- --- Name: teams_object_id_object_type_index; Type: INDEX; Schema: public; Owner: test --- - -CREATE INDEX teams_object_id_object_type_index ON public.teams USING btree (object_id, object_type); - - --- --- Name: team_members team_members_alias_id_foreign; 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); - - --- --- Name: team_members team_members_team_id_foreign; 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; - - --- --- Name: team_members team_members_user_id_foreign; 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; - - --- --- PostgreSQL database dump complete --- - diff --git a/cypress/dumps/initialState.sql b/cypress/dumps/initialState.sql index 54976467b5bd466564000a01a9e73389f6e596ba..943209725dc5fb3990a02ead7bcb4c509fa0b446 100644 --- a/cypress/dumps/initialState.sql +++ b/cypress/dumps/initialState.sql @@ -25,37 +25,118 @@ 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'; +-- +-- 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 -- @@ -287,6 +368,25 @@ CREATE TABLE public.users ( 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 -- @@ -326,7 +426,8 @@ INSERT INTO public.identities (id, user_id, created, updated, type, identifier, 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 ('2ac76834-4ddf-493a-8e50-16c3ecba1b94', '34785737-493d-4819-9982-f522abfaffe6', '2020-07-21 16:39:14.755+02', '2020-07-21 16:39:15.753+02', 'orcid', '0000-0003-1838-2441', 'Joanne Pilger', NULL, '{"accessToken": "fd3da810-1439-4666-ac1d-e737a8ba96bd", "refreshToken": "d11215a2-9921-4a74-be7e-2bec4946d1fb"}', 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); -- @@ -387,12 +488,29 @@ 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 ('0da0bbec-9261-4706-b990-0c10aa3cc6b4', '2020-07-21 16:35:06.125+02', '2020-07-21 16:35:24.978+02', NULL, NULL, '0000000276459921', NULL, NULL, NULL, NULL, 'user', NULL, 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-07-21 16:36:08.629+02', NULL, NULL, '0000000294294446', NULL, NULL, NULL, NULL, 'user', NULL, 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-21 16:39:03.909+02', NULL, NULL, '0000000159567341', NULL, NULL, NULL, NULL, 'user', NULL, 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 ('34785737-493d-4819-9982-f522abfaffe6', '2020-07-21 16:39:14.753+02', '2020-07-21 16:39:29.593+02', NULL, NULL, '0000000318382441', NULL, NULL, NULL, NULL, 'user', NULL, 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-21 16:40:03+02', NULL, NULL, '0000000205642016', NULL, NULL, NULL, NULL, 'user', NULL, 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-21 16:41:40.685+02', true, NULL, '0000000256415729', NULL, NULL, NULL, NULL, 'user', NULL, 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 ('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); + + +-- +-- 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); -- @@ -515,6 +633,48 @@ 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 -- diff --git a/cypress/integration/submission_spec.js b/cypress/integration/submission_spec.js index e897322d6ba42619225a8cb1a09b6fbec6282827..e3317c21b68d3de567ef6f275cf2d44f2d140ff5 100644 --- a/cypress/integration/submission_spec.js +++ b/cypress/integration/submission_spec.js @@ -1,3 +1,43 @@ +describe('URL submission test', () => { + it('can submit a URL and some metadata', () => { + cy.task('restore', 'initialState') + + cy.task('createToken', 'Emily Clay').then(token => { + cy.setToken(token) + cy.visit('/journal/dashboard') + }) + + cy.get('button') + .contains('New submission') + .click() + cy.get('button') + .contains('Submit a URL instead') + .click() + + cy.get('body').contains('Submission created') + cy.get('input[data-testid="meta.title"]') + .click() + .clear() + .type('My URL submission') + cy.get('[data-testid="submission.name"]') + .click() + .type('Emily Clay') + cy.get('[data-testid="submission.keywords"]') + .click() + .type('some, keywords') + + cy.get('button') + .contains('Submit your research object') + .click() + cy.get('button') + .contains('Submit your manuscript') + .click() + + cy.get('body').contains('My Submissions') + cy.get('body').contains('My URL submission') + }) +}) + // // TODO: What's with the wait? // const login = (username, password = 'password') => { @@ -241,3 +281,198 @@ // cy.contains('Nothing to do at the moment') // }) // }) + +// cy.task('db:seed') + +// cy.visit('/dashboard') + +// // 1. Log in as author +// login('author') + +// // 2. Submit a PDF +// cy.fixture('test-pdf.pdf', 'base64').then(fileContent => { +// cy.get('[data-testid="dropzone"]').upload( +// { +// fileContent, +// fileName: 'test-pdf.pdf', +// encoding: 'base64', +// mimeType: 'application/pdf', +// }, +// { subjectType: 'drag-n-drop' }, +// ) +// }) + +// cy.get('body').contains('Submission information') +// cy.get('[data-testid="meta.title"]').contains('test pdf') +// cy.get('[data-testid="meta.title"] div[contenteditable="true"]') +// .click() +// .type('{selectall}{del}A Manuscript For The Ages') + +// cy.get('[data-testid="meta.abstract"] div[contenteditable="true"]') +// .click() +// .type( +// `{selectall}{del}Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem.`, +// ) + +// cy.get('[data-testid="meta.keywords"]').type('quantum, machines, nature') + +// // TODO: Find a way to not match by partial class +// cy.get('[class*="Menu__Root"]').click() +// cy.get('[class*="Menu__Root"] [role="option"]:first').click() + +// // TODO: Find a way to remove the forces +// cy.get('[name="meta.articleSections"]:first').click({ force: true }) +// cy.get('[name="meta.declarations.openData"]:first').click({ force: true }) +// cy.get('[name="meta.declarations.previouslySubmitted"]:nth(1)').click({ +// force: true, +// }) +// cy.get('[name="meta.declarations.openPeerReview"]:first').click({ +// force: true, +// }) +// cy.get('[name="meta.declarations.streamlinedReview"]:nth(1)').click({ +// force: true, +// }) +// cy.get('[name="meta.declarations.researchNexus"]:nth(1)').click({ +// force: true, +// }) +// cy.get('[name="meta.declarations.preregistered"]:first').click({ +// force: true, +// }) + +// cy.get('[data-testid="suggestions.reviewers.suggested"]').type('Jane Doe') +// cy.get('[data-testid="suggestions.reviewers.opposed"]').type('James Doe') + +// cy.get('[data-testid="suggestions.editors.suggested"]').type('John Ode') +// cy.get('[data-testid="suggestions.editors.opposed"]').type('Gina Ode') + +// cy.get('[name="meta.notes.0.content"] div[contenteditable="true"]') +// .click() +// .type( +// 'This work was supported by the Trust [grant numbers 393,295]; the Natural Environment Research Council [grant number 49493].', +// ) +// cy.get('[name="meta.notes.1.content"] div[contenteditable="true"]') +// .click() +// .type('This is extremely divisive work, choose reviewers with care.') + +// cy.get('form button:last').click() +// cy.get('button[type="submit"]').click() + +// cy.visit('/dashboard') +// cy.contains('A Manuscript For The Ages') + +// // 3. Logout +// cy.get('nav button').click() + +// // 4. And login as admin +// login('admin', 'password') + +// cy.get('[data-testid="control-panel"]').click() + +// // 5. Assign senior editor +// // TODO: Find a way to not match by partial class +// cy.get('[class*="AssignEditor"] [class*="Menu__Root"]:first').click() +// cy.get( +// '[class*="AssignEditor"] [class*="Menu__Root"]:first [role="option"]:nth(1)', +// ).click() + +// // 6. Assign handling editor +// cy.get('[class*="AssignEditor"] [class*="Menu__Root"]:nth(1)').click() +// cy.get( +// '[class*="AssignEditor"] [class*="Menu__Root"]:nth(1) [role="option"]:nth(2)', +// ).click() + +// // 7. Logout +// cy.get('nav button').click() + +// // 8. And login as handling editor +// cy.wait(1000) +// login('heditor') +// cy.get('[data-testid="control-panel"]').click() + +// // 9. Assign reviewers +// cy.get('[class*="AssignEditorsReviewers"] a').click() + +// cy.get('.Select-control').click() +// cy.get('.Select-menu[role="listbox"] [role="option"]:nth(3)').click() +// cy.get('button[type="submit"]').click() +// cy.get('[class*="Reviewer__"]').should('have.length', 1) + +// cy.get('.Select-control').click() +// cy.get('.Select-menu[role="listbox"] [role="option"]:nth(4)').click() +// cy.get('button[type="submit"]').click() +// cy.get('[class*="Reviewer__"]').should('have.length', 2) + +// cy.get('.Select-control').click() +// cy.get('.Select-menu[role="listbox"] [role="option"]:nth(5)').click() +// cy.get('button[type="submit"]').click() +// cy.get('[class*="Reviewer__"]').should('have.length', 3) + +// // 10. Check that 3 reviewers are invited +// cy.contains('SimpleJ').click() +// cy.get('[data-testid="invited"]').contains('3') + +// // 11. Logout +// cy.get('nav button').click() + +// doReview( +// 'reviewer1', +// 'Great research into CC bases in the ky289 variant are mutated to TC which results in the truncation of the SAD-1.', +// 'Not too bad.', +// 0, +// ) +// doReview( +// 'reviewer2', +// 'Mediocre analysis of Iron-Sulfur ClUster assembly enzyme homolog.', +// 'It is so so.', +// 1, +// ) +// doReview( +// 'reviewer3', +// 'mTOR-Is positively influence the occurrence and course of certain tumors after solid organ transplantation.', +// 'It is not good.', +// 2, +// ) + +// // 12. Log in as handling editor +// login('heditor') +// cy.get('[data-testid="completed"]').contains('3') + +// cy.task('dump', '3reviewscompleted') +// }) + +// it('accept a submitted paper', () => { +// cy.task('restore', '3reviewscompleted') +// cy.visit('/dashboard') +// login('heditor') +// cy.get('[data-testid="completed"]').contains('3') + +// cy.get('[data-testid="control-panel"]').click() +// cy.contains('reviewer1') +// cy.contains('reviewer2') +// cy.contains('reviewer3') + +// // Write a decision +// cy.get('[placeholder*="Write/paste"] div[contenteditable="true"]') +// .focus() +// .type("Let's do this!") +// .blur() +// .wait(1000) +// cy.get(`[class*=Radio__Label]:nth(0)`) +// .click() +// .wait(1000) + +// cy.get('button[type=submit]').click() +// cy.wait(2000) +// cy.visit('/dashboard') +// cy.contains('accepted') +// }) + +// it('can delete a submission', () => { +// cy.task('restore', '3reviewscompleted') +// cy.visit('/dashboard') +// login('admin') +// cy.get('button:contains("Delete")').click() +// cy.visit('/dashboard') +// cy.contains('Nothing to do at the moment') +// }) +// }) diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js index 2bc079c1db0dc7ed3f50c8d459de4c38bdbff8a5..92a3a481f3c6041a92249012d4901767a50a3a69 100644 --- a/cypress/plugins/index.js +++ b/cypress/plugins/index.js @@ -26,6 +26,7 @@ const testUsers = { 'Joanne Pilger': '0000000318382441', 'Emily Clay': '0000000205642016', 'Sinead Sullivan': '0000000256415729', // admin + 'Test Account': '000000032536230X', } module.exports = (on, config) => { diff --git a/cypress/videos/login_spec.js.mp4 b/cypress/videos/login_spec.js.mp4 index a17ab4e7d55424434393a05e02f1aaaa37b7f914..1cb7e89d87a778ec752413299ba474fe4ec27672 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 new file mode 100644 index 0000000000000000000000000000000000000000..91c8214ea05c2ed0e929ae786e71f1296de36f34 Binary files /dev/null and b/cypress/videos/submission_spec.js.mp4 differ diff --git a/package.json b/package.json index e398f0d58051e11a65c262515b47e60876103e94..f7ebd6143fd07e164f4cf737277c00beb97ff14a 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "SimpleJ", - "version": "1.0.0", + "name": "Kotahi", + "version": "1.0.0-alpha", "private": true, - "description": "SimpleJ - open journals", + "description": "Kotahi - open journals", "license": "MIT", "engines": { "node": ">=9", @@ -113,7 +113,7 @@ "husky": "^0.14.3", "jest-cli": "^22.1.4", "joi-browser": "^10.0.6", - "lint-staged": "^4.1.3", + "lint-staged": "^10.2.11", "mini-css-extract-plugin": "^0.8.0", "node-dev": "^4.0.0", "prettier": "^1.8.2", @@ -137,7 +137,7 @@ }, "scripts": { "lint": "npm run lint:js && npm run lint:style", - "lint:js": "eslint app/ scripts/ server/ config/", + "lint:js": "eslint app/ scripts/ server/ config/ --ext js,jsx", "lint:style": "stylelint app/**/*.scss app/**/*.css", "precommit": "lint-staged", "reset": "pubsweet setupdb --clobber", @@ -164,12 +164,15 @@ "setupTestFrameworkScriptFile": "<rootDir>/test/helpers/jest-setup.js" }, "lint-staged": { - "*.js": [ - "prettier --write", - "git add" + "*.{js,jsx}": [ + "eslint --fix" ], - "*.css": "stylelint", - "*.scss": "stylelint" + "*.{js, jsx}": [ + "stylelint" + ], + "*.{json,md}": [ + "prettier --write" + ] }, "resolutions": { "bcrypt": "^3.0.6", diff --git a/profiles/testuser1.jpg b/profiles/testuser1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5f6a98e8bf6f175b23e370841e367e17d20ba2ac Binary files /dev/null and b/profiles/testuser1.jpg differ diff --git a/profiles/testuser2.jpg b/profiles/testuser2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fe7ee6a9870af88f7feeb23ff9053b0d594ab22e Binary files /dev/null and b/profiles/testuser2.jpg differ diff --git a/profiles/testuser3.jpg b/profiles/testuser3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..75d6e00f13d547b9530bc2388a9e739ae816393e Binary files /dev/null and b/profiles/testuser3.jpg differ diff --git a/profiles/testuser4.jpg b/profiles/testuser4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e7e0202e1f94b76444d6ca38ea22ba142460c38d Binary files /dev/null and b/profiles/testuser4.jpg differ diff --git a/profiles/testuser5.jpg b/profiles/testuser5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..be44ffd971d241f162ce2d1afc6cf44bc5ddeacf Binary files /dev/null and b/profiles/testuser5.jpg differ diff --git a/profiles/testuser6.jpg b/profiles/testuser6.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3bbb2c078c7e568b4704e1a481221e014542a3e5 Binary files /dev/null and b/profiles/testuser6.jpg differ diff --git a/profiles/testuser7.jpg b/profiles/testuser7.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cdcabc0fa587b1025469f8ed6f143ef57153c8da Binary files /dev/null and b/profiles/testuser7.jpg differ diff --git a/profiles/testuser8.jpg b/profiles/testuser8.jpg new file mode 100644 index 0000000000000000000000000000000000000000..27b8456293a420b472b74009a8cf6f3360085dfc Binary files /dev/null and b/profiles/testuser8.jpg differ diff --git a/scripts/clearAndSeed.js b/scripts/clearAndSeed.js index 3f343a85c7b128ff0863c72bc0a7237cb495e8c6..bdcdf51da9173e42dadc8afa08de57cc4cdf0152 100644 --- a/scripts/clearAndSeed.js +++ b/scripts/clearAndSeed.js @@ -34,7 +34,7 @@ const seed = async dumpSql => { await clearDb() await db.raw(dumpSql) logger.info('Cleared the database and restored from dump') - // TODO: This wait is necessary for the database to settle + // TODO: This wait is necessary for the database to "settle". await wait(2000) return true } diff --git a/server/app.js b/server/app.js index daf5ac7cb675e63ed57fc5062a4819dfb53ce93e..22ada082520d1dc728d3effe9d194c0bad60343f 100644 --- a/server/app.js +++ b/server/app.js @@ -60,8 +60,10 @@ const configureApp = app => { if (config.has('pubsweet-server.uploads')) { app.use( - '/uploads', - express.static(path.resolve(config.get('pubsweet-server.uploads'))), + '/static/uploads', + express.static( + path.join(__dirname, '..', config.get('pubsweet-server.uploads')), + ), ) } // Passport strategies diff --git a/server/model-file/src/resolvers.js b/server/model-file/src/resolvers.js index 83b647dcdc7021b66cdcdcd56efb651e733894bd..da8ea91c7c9c0b3e9a1423ac232df849defd7a10 100644 --- a/server/model-file/src/resolvers.js +++ b/server/model-file/src/resolvers.js @@ -1,10 +1,37 @@ +const crypto = require('crypto') +const { promisify } = require('util') +const fs = require('fs-extra') +const path = require('path') +const config = require('config') + const File = require('./file') +const randomBytes = promisify(crypto.randomBytes) +const uploadsPath = config.get('pubsweet-server').uploads + +const upload = async file => { + const { stream, filename, encoding } = await file + + const raw = await randomBytes(16) + const generatedFilename = raw.toString('hex') + path.extname(filename) + const outPath = path.join(uploadsPath, generatedFilename) + + await fs.ensureDir(uploadsPath) + const outStream = fs.createWriteStream(outPath) + stream.pipe(outStream, { encoding }) + + return new Promise((resolve, reject) => { + outStream.on('finish', () => resolve(outPath)) + outStream.on('error', reject) + }) +} const resolvers = { Query: {}, Mutation: { - async createFile(_, { id, file }, ctx) { - const data = await new File(file).save() + async createFile(_, { file, meta }, ctx) { + 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 9b70a794c1eb119ef525b4dde7be3caa29dd0524..34050c96a91107418bba4e9e5951163a8bc5dc72 100644 --- a/server/model-file/src/typeDefs.js +++ b/server/model-file/src/typeDefs.js @@ -1,6 +1,17 @@ const typeDefs = ` extend type Mutation { - createFile(file: Upload!): File! + # Using a separate variable because the Upload type hides other data + createFile(file: Upload!, meta: FileMetaInput): File! + } + + input FileMetaInput { + fileType: String + filename: String + mimeType: String + object: String + objectId: ID! + label: String + size: Int } type File implements Object { diff --git a/server/model-manuscript/src/graphql.js b/server/model-manuscript/src/graphql.js index e615f43bea6c281801fcd8f1e8f7ae9cd7eadf0a..25fdb53ae256593126a639974656061405cd3617 100644 --- a/server/model-manuscript/src/graphql.js +++ b/server/model-manuscript/src/graphql.js @@ -143,18 +143,74 @@ const resolvers = { }, async updateManuscript(_, { id, input }, ctx) { const data = JSON.parse(input) - const manuscript = await ctx.models.Manuscript.findById(id) + const manuscript = await ctx.models.Manuscript.query().findById(id) const update = merge({}, manuscript, data) - return ctx.models.Manuscript.update(id, update, ctx) + return ctx.models.Manuscript.query().updateAndFetchById(id, update) }, async makeDecision(_, { id, decision }, ctx) { - const manuscript = await ctx.models.Manuscript.findById(id) + const manuscript = await ctx.models.Manuscript.query().findById(id) manuscript.decision = decision manuscript.status = decision return manuscript.save() }, + async addReviewer(_, { manuscriptId, userId }, ctx) { + const manuscript = await ctx.models.Manuscript.query().findById( + manuscriptId, + ) + + const existingTeam = await manuscript + .$relatedQuery('teams') + .where('role', 'reviewer') + .first() + + // Add the reviewer to the existing team of reviewers + if (existingTeam) { + const reviewerExists = + (await existingTeam + .$relatedQuery('users') + .where('users.id', userId) + .resultSize()) > 0 + if (!reviewerExists) { + await new ctx.models.TeamMember({ + teamId: existingTeam.id, + status: 'invited', + userId, + }).save() + } + return existingTeam.$query().eager('members.[user]') + } + + // Create a new team of reviewers if it doesn't exist + const newTeam = await new ctx.models.Team({ + objectId: manuscriptId, + objectType: 'Manuscript', + members: [{ status: 'invited', userId }], + role: 'reviewer', + }).saveGraph() + + return newTeam + }, + async removeReviewer(_, { manuscriptId, userId }, ctx) { + const manuscript = await ctx.models.Manuscript.query().findById( + manuscriptId, + ) + + const reviewerTeam = await manuscript + .$relatedQuery('teams') + .where('role', 'reviewer') + .first() + + await ctx.models.TeamMember.query() + .where({ + userId, + teamId: reviewerTeam.id, + }) + .delete() + + return reviewerTeam.$query().eager('members.[user]') + }, }, Query: { async manuscript(_, { id }, ctx) { @@ -277,6 +333,8 @@ const typeDefs = ` deleteManuscript(id: ID!): ID! reviewerResponse(currentUserId: ID, action: String, teamId: ID! ): Team assignTeamEditor(id: ID!, input: String): [Team] + addReviewer(manuscriptId: ID!, userId: ID!): Team + removeReviewer(manuscriptId: ID!, userId: ID!): Team } type Manuscript implements Object { diff --git a/server/model-review/src/resolvers.js b/server/model-review/src/resolvers.js index 90b611e5b4d76d6fc766d058016f5618cd776adb..9a8fc508e4d9f8d9e78e90e6e26bc7e8a9d8882a 100644 --- a/server/model-review/src/resolvers.js +++ b/server/model-review/src/resolvers.js @@ -5,11 +5,13 @@ const resolvers = { Mutation: { async updateReview(_, { id, input }, ctx) { if (id) { - const review = await ctx.connectors.Review.fetchOne(id, ctx) + const review = await ctx.models.Review.query().findById(id) const update = merge({}, review, input) - await ctx.connectors.Review.update(id, update, ctx) // Load Review - const rvw = await new Review(update) + const rvw = await ctx.models.Review.query().updateAndFetchById( + id, + update, + ) rvw.comments = await rvw.getComments() return rvw @@ -21,6 +23,25 @@ const resolvers = { return review }, + + async completeReview(_, { id }, ctx) { + const review = await ctx.models.Review.query().findById(id) + const manuscript = await ctx.models.Manuscript.query().findById( + review.manuscriptId, + ) + const team = await manuscript + .$relatedQuery('teams') + .where('role', 'reviewer') + .first() + + const member = await team + .$relatedQuery('members') + .where('userId', ctx.user.id) + .first() + + member.status = 'completed' + return member.save() + }, }, } diff --git a/server/model-review/src/typeDefs.js b/server/model-review/src/typeDefs.js index 0ded633743461e8c3a4fb01fc3d58ca4b5815f43..fb39b5e3903bb16633a2860330a201fa9a683165 100644 --- a/server/model-review/src/typeDefs.js +++ b/server/model-review/src/typeDefs.js @@ -1,9 +1,10 @@ const typeDefs = ` extend type Mutation { updateReview(id: ID, input: ReviewInput): Review! + completeReview(id: ID!): TeamMember } - type Review implements Object { + type Review implements Object { id: ID! created: DateTime! updated: DateTime diff --git a/server/model-team/src/graphql.js b/server/model-team/src/graphql.js index 0f1b08e636c84e1152f854635976f1877c67c873..ed95c10c5e82b7c21915ba55dc57bd020e5d6c30 100644 --- a/server/model-team/src/graphql.js +++ b/server/model-team/src/graphql.js @@ -98,7 +98,7 @@ const typeDefs = ` id: ID! type: String! role: String! - name: String! + name: String object: TeamObject members: [TeamMember!] owners: [User] diff --git a/server/model-team/src/team_member.js b/server/model-team/src/team_member.js index 51236eff1e88fb38c0b01d77d90d400a0e7a0812..eff8dd732633a4249c9913b8f2ac221dec14dded 100644 --- a/server/model-team/src/team_member.js +++ b/server/model-team/src/team_member.js @@ -43,7 +43,6 @@ class TeamMember extends BaseModel { teamId: { type: 'string', format: 'uuid' }, aliasId: { type: ['string', 'null'], format: 'uuid' }, status: { type: 'string' }, - global: { type: ['boolean', 'null'] }, }, } } diff --git a/server/model-user/src/graphql.js b/server/model-user/src/graphql.js index 56565dd4a4645070bf9d86913cd82d012e87b102..241ca795f8d0622f0da7d0ff144ca53a494ea721 100644 --- a/server/model-user/src/graphql.js +++ b/server/model-user/src/graphql.js @@ -142,16 +142,16 @@ const resolvers = { return identities }, }, - LocalIdentity: { - __isTypeOf: (obj, context, info) => obj.type === 'local', - async email(obj, args, ctx, info) { - // Emails stored on user, but surfaced in local identity too - return (await ctx.loaders.User.load(obj.userId)).email - }, - }, - ExternalIdentity: { - __isTypeOf: (obj, context, info) => obj.type !== 'local', - }, + // LocalIdentity: { + // __isTypeOf: (obj, context, info) => obj.type === 'local', + // async email(obj, args, ctx, info) { + // // Emails stored on user, but surfaced in local identity too + // return (await ctx.loaders.User.load(obj.userId)).email + // }, + // }, + // ExternalIdentity: { + // __isTypeOf: (obj, context, info) => obj.type !== 'local', + // }, } const typeDefs = ` @@ -209,33 +209,34 @@ const typeDefs = ` roles: [String] } - interface Identity { + type Identity { id: ID name: String aff: String # JATS <aff> email: String # JATS <aff> type: String + identifier: String } # union Identity = Local | External # local identity (not from ORCID, etc.) - type LocalIdentity implements Identity { - id: ID - name: String - email: String - aff: String - type: String - } - - type ExternalIdentity implements Identity { - id: ID - name: String - identifier: String - email: String - aff: String - type: String - } + #type LocalIdentity implements Identity { + # id: ID + # name: String + # email: String + # aff: String + # type: String + #} + # + #type ExternalIdentity implements Identity { + # id: ID + # name: String + # identifier: String + # email: String + # aff: String + # type: String + #} input UserInput { username: String! diff --git a/webpack/plugins.js b/webpack/plugins.js index 9eac16ed92425ec318a6aa263838c67c17bfa35c..9a2c29675264d7660c14ee29a71d195f44e4c003 100644 --- a/webpack/plugins.js +++ b/webpack/plugins.js @@ -18,7 +18,7 @@ module.exports = (opts = {}) => { if (opts.html) { plugins.push( new HtmlWebpackPlugin({ - title: 'SimpleJ - open journals', + title: 'Kotahi - open journals', template: '../app/index.ejs', // Load a custom template }), ) diff --git a/webpack/webpack.config.js b/webpack/webpack.config.js index 0f9449a6d39e559a60b3ad841f007cafe8077143..5f3ab73a5b07cd0abf21bfe529016907e2e5542d 100644 --- a/webpack/webpack.config.js +++ b/webpack/webpack.config.js @@ -37,7 +37,7 @@ module.exports = webpackEnv => { '/api': 'http://localhost:3000', '/auth': 'http://localhost:3000', '/graphql': 'http://localhost:3000', - '/uploads': 'http://locahost:3000', + '/static/uploads': 'http://locahost:3000', '/static/profiles': 'http://localhost:3000', '/public': 'http://localhost:3000', }, diff --git a/yarn.lock b/yarn.lock index 7f7ed6a8d66ffc574717b5fb5ba1eafeca60d319..7e8a7f476b6fb1be7ab5bee45bc8a7bbff7cd1b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1945,6 +1945,11 @@ "@types/connect" "*" "@types/node" "*" +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + "@types/connect@*": version "3.4.33" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" @@ -2496,16 +2501,23 @@ ansi-colors@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== -ansi-escapes@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== ansi-escapes@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== +ansi-escapes@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" @@ -2526,6 +2538,11 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -2538,6 +2555,14 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + any-base@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/any-base/-/any-base-1.1.0.tgz#ae101a62bc08a597b4c9ab5b7089d456630549fe" @@ -2827,11 +2852,6 @@ apollo-utilities@1.3.4, apollo-utilities@^1.0.1, apollo-utilities@^1.2.1, apollo ts-invariant "^0.4.0" tslib "^1.10.0" -app-root-path@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" - integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== - append-field@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" @@ -3044,6 +3064,11 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-each@^1.0.0, async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -3858,7 +3883,7 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@~3.0.2: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -4258,7 +4283,7 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4. escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -4269,6 +4294,14 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +chalk@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + change-emitter@^0.1.2: version "0.1.6" resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515" @@ -4466,10 +4499,12 @@ cli-cursor@^2.0.0, cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-spinners@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" - integrity sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw= +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" cli-table3@0.5.1: version "0.5.1" @@ -4481,6 +4516,14 @@ cli-table3@0.5.1: optionalDependencies: colors "^1.1.2" +cli-truncate@2.1.0, cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -4587,12 +4630,19 @@ color-convert@^1.3.0, color-convert@^1.9.0, color-convert@^1.9.1: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@^1.0.0: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -4685,7 +4735,7 @@ commander@4.1.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -commander@^2.11.0, commander@^2.18.0, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@^2.9.0, commander@~2.20.3: +commander@^2.18.0, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@~2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -4918,20 +4968,6 @@ cors@^2.8.4: object-assign "^4" vary "^1" -cosmiconfig@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-1.1.0.tgz#0dea0f9804efdfb929fbb1b188e25553ea053d37" - integrity sha1-DeoPmATv37kp+7GxiOJVU+oFPTc= - dependencies: - graceful-fs "^4.1.2" - js-yaml "^3.4.3" - minimist "^1.2.0" - object-assign "^4.0.1" - os-homedir "^1.0.1" - parse-json "^2.2.0" - pinkie-promise "^2.0.0" - require-from-string "^1.1.0" - cosmiconfig@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-3.1.0.tgz#640a94bf9847f321800403cd273af60665c73397" @@ -5365,6 +5401,11 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + deep-equal@*: version "2.0.3" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0" @@ -5832,6 +5873,11 @@ emoji-regex@^7.0.1: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + emoji-regex@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.0.0.tgz#48a2309cc8a1d2e9d23bc6a67c39b63032e76ea4" @@ -5893,6 +5939,13 @@ enhanced-resolve@^4.1.0, enhanced-resolve@^4.1.1: memory-fs "^0.5.0" tapable "^1.0.0" +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + entities@^1.1.1, entities@~1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" @@ -6437,18 +6490,20 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" - integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo= +execa@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2" + integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A== dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" execall@^1.0.0: version "1.0.0" @@ -6766,6 +6821,13 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" @@ -8094,13 +8156,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - indent-string@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" @@ -8449,6 +8504,11 @@ is-fullwidth-code-point@^2.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-function@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" @@ -8970,11 +9030,6 @@ jest-environment-node@^22.4.1: jest-mock "^22.4.3" jest-util "^22.4.3" -jest-get-type@^21.2.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23" - integrity sha512-y2fFw3C+D0yjNSDp7ab1kcd6NUYfy3waPTlD8yWkAtiocJdBRQqNoRqVfMNxgj+IjT0V5cBIHJO0z9vuSSZ43Q== - jest-get-type@^22.1.0, jest-get-type@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" @@ -9135,16 +9190,6 @@ jest-util@^22.4.1, jest-util@^22.4.3: mkdirp "^0.5.1" source-map "^0.6.0" -jest-validate@^21.1.0: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7" - integrity sha512-k4HLI1rZQjlU+EC682RlQ6oZvLrE5SCh3brseQc24vbZTxzT/k/3urar5QMCVgjadmSO7lECeGdc6YxnM3yEGg== - dependencies: - chalk "^2.0.1" - jest-get-type "^21.2.0" - leven "^2.1.0" - pretty-format "^21.2.1" - jest-validate@^22.4.4: version "22.4.4" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.4.4.tgz#1dd0b616ef46c995de61810d85f57119dbbcec4d" @@ -9209,7 +9254,7 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -js-yaml@^3.4.3, js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1: +js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1: version "3.14.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== @@ -9656,46 +9701,32 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -lint-staged@^4.1.3: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-4.3.0.tgz#ed0779ad9a42c0dc62bb3244e522870b41125879" - integrity sha512-C/Zxslg0VRbsxwmCu977iIs+QyrmW2cyRCPUV5NDFYOH/jtRFHH8ch7ua2fH0voI/nVC3Tpg7DykfgMZySliKw== +lint-staged@^10.2.11: + version "10.2.11" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.11.tgz#713c80877f2dc8b609b05bc59020234e766c9720" + integrity sha512-LRRrSogzbixYaZItE2APaS4l2eJMjjf5MbclRZpLJtcQJShcvUzKXsNeZgsLIZ0H0+fg2tL4B59fU9wHIHtFIA== dependencies: - app-root-path "^2.0.0" - chalk "^2.1.0" - commander "^2.11.0" - cosmiconfig "^1.1.0" - execa "^0.8.0" - is-glob "^4.0.0" - jest-validate "^21.1.0" - listr "^0.12.0" - lodash "^4.17.4" - log-symbols "^2.0.0" - minimatch "^3.0.0" - npm-which "^3.0.1" - p-map "^1.1.1" - staged-git-files "0.0.4" - stringify-object "^3.2.0" + chalk "^4.0.0" + cli-truncate "2.1.0" + commander "^5.1.0" + cosmiconfig "^6.0.0" + debug "^4.1.1" + dedent "^0.7.0" + enquirer "^2.3.5" + execa "^4.0.1" + listr2 "^2.1.0" + log-symbols "^4.0.0" + micromatch "^4.0.2" + normalize-path "^3.0.0" + please-upgrade-node "^3.2.0" + string-argv "0.3.1" + stringify-object "^3.3.0" listr-silent-renderer@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= -listr-update-renderer@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9" - integrity sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - strip-ansi "^3.0.1" - listr-update-renderer@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" @@ -9710,16 +9741,6 @@ listr-update-renderer@^0.5.0: log-update "^2.3.0" strip-ansi "^3.0.1" -listr-verbose-renderer@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" - integrity sha1-ggb0z21S3cWCfl/RSYng6WWTOjU= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - listr-verbose-renderer@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" @@ -9730,6 +9751,20 @@ listr-verbose-renderer@^0.5.0: date-fns "^1.27.2" figures "^2.0.0" +listr2@^2.1.0: + version "2.3.3" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.3.3.tgz#ee9f4d95dd325ad1ae89fe7327f2a4f4498224b5" + integrity sha512-vU2eiFEzUEaDjgwRJ/n8RB79K2cBcaTw1DigIGHnXGp/BEeQqxGwiM8R17Itit5l2ykrrST11kw2l9vSpCbqUQ== + dependencies: + chalk "^4.0.0" + cli-truncate "^2.1.0" + figures "^3.2.0" + indent-string "^4.0.0" + log-update "^4.0.0" + p-map "^4.0.0" + rxjs "^6.5.5" + through "^2.3.8" + listr@0.14.3: version "0.14.3" resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" @@ -9745,28 +9780,6 @@ listr@0.14.3: p-map "^2.0.0" rxjs "^6.3.3" -listr@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a" - integrity sha1-a84sD1YD+klYDqF81qAMwOX6RRo= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - figures "^1.7.0" - indent-string "^2.1.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.2.0" - listr-verbose-renderer "^0.4.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - ora "^0.2.3" - p-map "^1.1.1" - rxjs "^5.0.0-beta.11" - stream-to-observable "^0.1.0" - strip-ansi "^3.0.1" - load-bmfont@^1.3.1, load-bmfont@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.0.tgz#75f17070b14a8c785fe7f5bee2e6fd4f98093b6b" @@ -9997,13 +10010,12 @@ log-symbols@^2.0.0: dependencies: chalk "^2.0.1" -log-update@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" - integrity sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE= +log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== dependencies: - ansi-escapes "^1.0.0" - cli-cursor "^1.0.2" + chalk "^4.0.0" log-update@^2.3.0: version "2.3.0" @@ -10014,6 +10026,16 @@ log-update@^2.3.0: cli-cursor "^2.0.0" wrap-ansi "^3.0.1" +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + loglevel@^1.6.7, loglevel@^1.6.8: version "1.6.8" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" @@ -10309,6 +10331,14 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -10384,7 +10414,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: +minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -10904,13 +10934,6 @@ npm-packlist@^1.1.6: npm-bundled "^1.0.1" npm-normalize-package-bin "^1.0.1" -npm-path@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.4.tgz#c641347a5ff9d6a09e4d9bce5580c4f505278e64" - integrity sha512-IFsj0R9C7ZdR5cP+ET342q77uSRdtWOlWpih5eC+lu29tIDbNEgDbzgVJ5UFvYHWhxDZ5TFkJafFioO0pPQjCw== - dependencies: - which "^1.2.10" - npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -10925,15 +10948,6 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -npm-which@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa" - integrity sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo= - dependencies: - commander "^2.9.0" - npm-path "^2.0.2" - which "^1.2.10" - npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -11223,16 +11237,6 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" word-wrap "~1.2.3" -ora@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" - integrity sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q= - dependencies: - chalk "^1.1.1" - cli-cursor "^1.0.2" - cli-spinners "^0.1.2" - object-assign "^4.0.1" - orderedmap@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-1.1.1.tgz#c618e77611b3b21d0fe3edc92586265e0059c789" @@ -11250,7 +11254,7 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0, os-homedir@^1.0.1: +os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= @@ -11327,11 +11331,6 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-map@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" - integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== - p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" @@ -11344,6 +11343,13 @@ p-map@^3.0.0: dependencies: aggregate-error "^3.0.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-retry@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" @@ -11789,7 +11795,7 @@ phin@^2.9.1: resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c" integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA== -picomatch@^2.0.4, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== @@ -11854,6 +11860,13 @@ pkginfo@0.3.x: resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21" integrity sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE= +please-upgrade-node@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" + integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg== + dependencies: + semver-compare "^1.0.0" + pluralize@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" @@ -12292,14 +12305,6 @@ pretty-error@^2.0.2: renderkid "^2.0.1" utila "~0.4" -pretty-format@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-21.2.1.tgz#ae5407f3cf21066cd011aa1ba5fce7b6a2eddb36" - integrity sha512-ZdWPGYAnYfcVP8yKA3zFjCn8s4/17TeYH28MXuC8vTp0o21eXjbFGcOAXZEaDaOFJjc3h2qa7HQNHNshhvoh2A== - dependencies: - ansi-regex "^3.0.0" - ansi-styles "^3.2.0" - pretty-format@^22.4.0, pretty-format@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f" @@ -13566,11 +13571,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^1.1.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" - integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= - require-from-string@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" @@ -13667,6 +13667,14 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -13746,13 +13754,6 @@ rx-lite@*, rx-lite@^4.0.8: resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= -rxjs@^5.0.0-beta.11: - version "5.5.12" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" - integrity sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw== - dependencies: - symbol-observable "1.0.1" - rxjs@^6.3.3: version "6.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" @@ -13882,6 +13883,11 @@ selfsigned@^1.10.7: dependencies: node-forge "0.9.0" +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= + "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -14089,6 +14095,24 @@ slice-ansi@1.0.0: dependencies: is-fullwidth-code-point "^2.0.0" +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -14332,11 +14356,6 @@ stack-utils@^1.0.1: resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== -staged-git-files@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-0.0.4.tgz#d797e1b551ca7a639dec0237dc6eb4bb9be17d35" - integrity sha1-15fhtVHKemOd7AI33G60u5vhfTU= - start-server-and-test@^1.10.6: version "1.11.2" resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.11.2.tgz#9144b7b6f25197148f159f261ae80119afbb17d5" @@ -14412,11 +14431,6 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -stream-to-observable@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" - integrity sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4= - streamifier@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/streamifier/-/streamifier-0.1.1.tgz#97e98d8fa4d105d62a2691d1dc07e820db8dfc4f" @@ -14432,6 +14446,11 @@ strict-uri-encode@^1.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= +string-argv@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + string-length@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" @@ -14474,6 +14493,15 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + string.prototype.matchall@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e" @@ -14540,7 +14568,7 @@ stringify-entities@^1.0.1: is-alphanumerical "^1.0.0" is-hexadecimal "^1.0.0" -stringify-object@^3.2.0: +stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== @@ -14570,6 +14598,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + strip-bom@3.0.0, strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -14781,7 +14816,7 @@ supertest@^3.0.0: methods "^1.1.2" superagent "^3.8.3" -supports-color@7.1.0: +supports-color@7.1.0, supports-color@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== @@ -14832,11 +14867,6 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" -symbol-observable@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" - integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= - symbol-observable@^1.0.2, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -14962,7 +14992,7 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -through@2, through@^2.3.6, through@~2.3, through@~2.3.1: +through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -15204,6 +15234,11 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + type-is@^1.6.16, type-is@^1.6.4, type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -16083,7 +16118,7 @@ which-typed-array@^1.1.2: has-symbols "^1.0.1" is-typed-array "^1.1.3" -which@^1.2.10, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -16188,6 +16223,15 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"