From 952a1340c40337594d8390c3861640b176686f4f Mon Sep 17 00:00:00 2001 From: Alf Eaton <eaton.alf@gmail.com> Date: Thu, 17 Aug 2017 14:12:09 +0100 Subject: [PATCH] Restart as a monorepo --- .envrc | 6 +- .eslintrc | 19 +- .gitignore | 6 +- .nvmrc | 2 +- README.md | 17 +- app/app.js | 33 -- app/components/App.js | 18 - app/components/Navigation.js | 37 -- app/components/NavigationContainer.js | 32 -- app/components/WaxContainer.js | 78 ---- app/routes.js | 39 -- app/styles.js | 5 - app/styles/_base.scss | 26 -- app/styles/_bootstrap.scss | 24 - app/styles/_bootstrap_buttons.scss | 12 - app/styles/_bootstrap_form.scss | 31 -- app/styles/_fonts.scss | 12 - app/styles/_icons.scss | 2 - app/styles/_modal.scss | 68 --- app/styles/_navbar.scss | 73 --- app/styles/_reset.scss | 136 ------ app/styles/_table.scss | 98 ---- app/styles/_variables.scss | 10 - app/styles/main.scss | 18 - config/shared.js | 66 --- docs/schema-outline.ftml | 419 ------------------ docs/workflow-outline.ftml | 121 ----- lerna.json | 7 + package.json | 92 +--- packages/collabra/.gitignore | 6 + packages/collabra/app/app.js | 33 ++ packages/collabra/app/components/App.js | 77 ++++ .../collabra/app/components/App.local.css | 3 + .../app/components/AuthenticatedPage.js | 30 +- packages/collabra/app/components/Logout.js | 37 ++ .../collabra/app/components/SubmitPage.js | 18 + .../collabra/app/config/journal/decisions.js | 22 + .../app/config/journal/declarations.js | 81 ++++ packages/collabra/app/config/journal/index.js | 4 + .../collabra/app/config/journal/metadata.js | 4 + .../collabra/app/config/journal/sections.js | 6 + {app => packages/collabra/app}/index.ejs | 0 {app => packages/collabra/app}/index.html | 0 packages/collabra/app/routes.js | 37 ++ packages/collabra/config/authsome.js | 8 + .../collabra/config}/components.json | 5 +- {config => packages/collabra/config}/dev.js | 0 .../collabra/config}/development.js | 0 .../collabra/config}/production.js | 0 packages/collabra/config/shared.js | 32 ++ {config => packages/collabra/config}/test.js | 0 packages/collabra/config/validations.js | 15 + packages/collabra/package.json | 67 +++ .../collabra/static}/pubsweet-rgb-small.jpg | Bin .../collabra/static}/pubsweet.jpg | Bin packages/collabra/webpack/common-rules.js | 105 +++++ .../collabra/webpack}/webpack.dev.config.js | 16 +- .../webpack}/webpack.production.config.js | 15 +- .../collabra/webpack}/webpack.test.config.js | 12 +- packages/component-dashboard/package.json | 26 ++ .../src/components/Dashboard.js | 24 + .../src/components/Dashboard.local.css | 4 + .../src/components/DashboardItem.js | 9 + .../src/components/DashboardPage.js | 22 + .../src/components/UploadManuscript.js | 94 ++++ .../src/components/UploadManuscript.local.css | 26 ++ .../src/components/index.js | 1 + packages/component-dashboard/src/index.js | 7 + packages/component-manuscript/package.json | 43 ++ .../src/components/Manuscript.js | 26 ++ .../src/components/Manuscript.local.css | 9 + .../src/components/ManuscriptPage.js | 19 + .../src/components/index.js | 1 + packages/component-manuscript/src/index.js | 7 + .../component-manuscript/webpack.config.js | 86 ++++ packages/component-submit/package.json | 25 ++ .../component-submit/src/components/Submit.js | 9 + .../src/components/Submit.local.css | 3 + .../src/components/SubmitPage.js | 17 + .../component-submit/src/components/index.js | 1 + packages/component-submit/src/index.js | 7 + packages/component-submit/src/lib/date.js | 3 + packages/component-submit/src/lib/sort.js | 3 + packages/component-submit/src/lib/text.js | 1 + packages/xpub-fonts/package.json | 12 + packages/xpub-fonts/src/index.js | 3 + packages/xpub-selectors/package.json | 7 + packages/xpub-selectors/src/index.js | 14 + packages/xpub-ui/.eslintrc | 7 + .../lib/styleguide/StyleGuideRenderer.js | 25 ++ .../styleguide/StyleGuideRenderer.local.css | 37 ++ packages/xpub-ui/lib/styleguide/Wrapper.js | 21 + .../xpub-ui/lib/styleguide/Wrapper.local.css | 3 + packages/xpub-ui/lib/styleguide/Wrapper.scss | 4 + packages/xpub-ui/package.json | 34 ++ packages/xpub-ui/src/AppBar.js | 27 ++ packages/xpub-ui/src/AppBar.local.css | 16 + packages/xpub-ui/src/AppBar.md | 20 + packages/xpub-ui/src/Radio.js | 18 + packages/xpub-ui/src/Radio.local.css | 13 + packages/xpub-ui/src/Radio.md | 25 ++ packages/xpub-ui/src/RadioGroup.js | 19 + packages/xpub-ui/src/RadioGroup.md | 26 ++ packages/xpub-ui/src/YesOrNo.js | 25 ++ packages/xpub-ui/src/YesOrNo.local.css | 3 + packages/xpub-ui/src/YesOrNo.md | 10 + packages/xpub-ui/src/index.js | 2 + packages/xpub-ui/styleguide.config.js | 23 + packages/xpub-ui/webpack.config.js | 78 ++++ permissions/index.js | 3 - webpack/common-rules.js | 114 ----- webpack/components.js | 8 - 112 files changed, 1592 insertions(+), 1618 deletions(-) delete mode 100644 app/app.js delete mode 100644 app/components/App.js delete mode 100644 app/components/Navigation.js delete mode 100644 app/components/NavigationContainer.js delete mode 100644 app/components/WaxContainer.js delete mode 100644 app/routes.js delete mode 100644 app/styles.js delete mode 100644 app/styles/_base.scss delete mode 100644 app/styles/_bootstrap.scss delete mode 100644 app/styles/_bootstrap_buttons.scss delete mode 100644 app/styles/_bootstrap_form.scss delete mode 100644 app/styles/_fonts.scss delete mode 100644 app/styles/_icons.scss delete mode 100644 app/styles/_modal.scss delete mode 100644 app/styles/_navbar.scss delete mode 100644 app/styles/_reset.scss delete mode 100644 app/styles/_table.scss delete mode 100644 app/styles/_variables.scss delete mode 100644 app/styles/main.scss delete mode 100644 config/shared.js delete mode 100644 docs/schema-outline.ftml delete mode 100644 docs/workflow-outline.ftml create mode 100644 lerna.json create mode 100644 packages/collabra/.gitignore create mode 100644 packages/collabra/app/app.js create mode 100644 packages/collabra/app/components/App.js create mode 100644 packages/collabra/app/components/App.local.css rename app/components/AuthenticatedContainer.js => packages/collabra/app/components/AuthenticatedPage.js (69%) create mode 100644 packages/collabra/app/components/Logout.js create mode 100644 packages/collabra/app/components/SubmitPage.js create mode 100644 packages/collabra/app/config/journal/decisions.js create mode 100644 packages/collabra/app/config/journal/declarations.js create mode 100644 packages/collabra/app/config/journal/index.js create mode 100644 packages/collabra/app/config/journal/metadata.js create mode 100644 packages/collabra/app/config/journal/sections.js rename {app => packages/collabra/app}/index.ejs (100%) rename {app => packages/collabra/app}/index.html (100%) create mode 100644 packages/collabra/app/routes.js create mode 100644 packages/collabra/config/authsome.js rename {config => packages/collabra/config}/components.json (58%) rename {config => packages/collabra/config}/dev.js (100%) rename {config => packages/collabra/config}/development.js (100%) rename {config => packages/collabra/config}/production.js (100%) create mode 100644 packages/collabra/config/shared.js rename {config => packages/collabra/config}/test.js (100%) create mode 100644 packages/collabra/config/validations.js create mode 100644 packages/collabra/package.json rename {static => packages/collabra/static}/pubsweet-rgb-small.jpg (100%) rename {static => packages/collabra/static}/pubsweet.jpg (100%) create mode 100644 packages/collabra/webpack/common-rules.js rename {webpack => packages/collabra/webpack}/webpack.dev.config.js (81%) rename {webpack => packages/collabra/webpack}/webpack.production.config.js (87%) rename {webpack => packages/collabra/webpack}/webpack.test.config.js (90%) create mode 100644 packages/component-dashboard/package.json create mode 100644 packages/component-dashboard/src/components/Dashboard.js create mode 100644 packages/component-dashboard/src/components/Dashboard.local.css create mode 100644 packages/component-dashboard/src/components/DashboardItem.js create mode 100644 packages/component-dashboard/src/components/DashboardPage.js create mode 100644 packages/component-dashboard/src/components/UploadManuscript.js create mode 100644 packages/component-dashboard/src/components/UploadManuscript.local.css create mode 100644 packages/component-dashboard/src/components/index.js create mode 100644 packages/component-dashboard/src/index.js create mode 100644 packages/component-manuscript/package.json create mode 100644 packages/component-manuscript/src/components/Manuscript.js create mode 100644 packages/component-manuscript/src/components/Manuscript.local.css create mode 100644 packages/component-manuscript/src/components/ManuscriptPage.js create mode 100644 packages/component-manuscript/src/components/index.js create mode 100644 packages/component-manuscript/src/index.js create mode 100644 packages/component-manuscript/webpack.config.js create mode 100644 packages/component-submit/package.json create mode 100644 packages/component-submit/src/components/Submit.js create mode 100644 packages/component-submit/src/components/Submit.local.css create mode 100644 packages/component-submit/src/components/SubmitPage.js create mode 100644 packages/component-submit/src/components/index.js create mode 100644 packages/component-submit/src/index.js create mode 100644 packages/component-submit/src/lib/date.js create mode 100644 packages/component-submit/src/lib/sort.js create mode 100644 packages/component-submit/src/lib/text.js create mode 100644 packages/xpub-fonts/package.json create mode 100644 packages/xpub-fonts/src/index.js create mode 100644 packages/xpub-selectors/package.json create mode 100644 packages/xpub-selectors/src/index.js create mode 100644 packages/xpub-ui/.eslintrc create mode 100644 packages/xpub-ui/lib/styleguide/StyleGuideRenderer.js create mode 100644 packages/xpub-ui/lib/styleguide/StyleGuideRenderer.local.css create mode 100644 packages/xpub-ui/lib/styleguide/Wrapper.js create mode 100644 packages/xpub-ui/lib/styleguide/Wrapper.local.css create mode 100644 packages/xpub-ui/lib/styleguide/Wrapper.scss create mode 100644 packages/xpub-ui/package.json create mode 100644 packages/xpub-ui/src/AppBar.js create mode 100644 packages/xpub-ui/src/AppBar.local.css create mode 100644 packages/xpub-ui/src/AppBar.md create mode 100644 packages/xpub-ui/src/Radio.js create mode 100644 packages/xpub-ui/src/Radio.local.css create mode 100644 packages/xpub-ui/src/Radio.md create mode 100644 packages/xpub-ui/src/RadioGroup.js create mode 100644 packages/xpub-ui/src/RadioGroup.md create mode 100644 packages/xpub-ui/src/YesOrNo.js create mode 100644 packages/xpub-ui/src/YesOrNo.local.css create mode 100644 packages/xpub-ui/src/YesOrNo.md create mode 100644 packages/xpub-ui/src/index.js create mode 100644 packages/xpub-ui/styleguide.config.js create mode 100644 packages/xpub-ui/webpack.config.js delete mode 100644 permissions/index.js delete mode 100644 webpack/common-rules.js delete mode 100644 webpack/components.js diff --git a/.envrc b/.envrc index 41c57fe011..7e21fc2f6d 100644 --- a/.envrc +++ b/.envrc @@ -1,3 +1,3 @@ -if declare -Ff use_nvm >/dev/null; then - use nvm -fi +#https://github.com/direnv/direnv/wiki/Node#load-nodejs-version-from-a-node-version-or-nvmrc-file +set -e +use node diff --git a/.eslintrc b/.eslintrc index e8d0389584..5e603ecd19 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,20 +1,3 @@ { - "parser": "babel-eslint", - "extends": [ - "standard", - "plugin:react/recommended" - ], - "plugins": [ - "react" - ], - "parserOptions": { - "ecmaVersion": 7, - "ecmaFeatures": { - "jsx": true - } - }, - "env": { - "es6": true, - "browser": true - } + "extends": "react-app" } diff --git a/.gitignore b/.gitignore index 1be6079c9d..2053ddd9dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -api -logs -node_modules +*.log +.env .env.* -_build diff --git a/.nvmrc b/.nvmrc index c8357abfec..7f8f011eb7 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -7.9 \ No newline at end of file +7 diff --git a/README.md b/README.md index 5a01649695..f5b8ddc9b8 100644 --- a/README.md +++ b/README.md @@ -1 +1,16 @@ -Note: xpub is still _very_ new. This repository contains components for uploading and submitting a manuscript, but is not yet ready for use. +Note: xpub is still _very_ new. This repository contains an initial set of components but is not yet ready for use. + +## Installing + +In the root directory, run `npm install` then `npm run hoist` to install all the dependencies. + +## Contents + +* `collabra`: a PubSweet application that provides configuration and routing. +* `component-dashboard`: a PubSweet component that provides a Dashboard page. +* `component-submit`: a PubSweet component that provides a Submit page. +* `component-manuscript`: a PubSweet component that provides a Manuscript page. +* `xpub-fonts`: an index that imports the fonts for xpub applications +* `xpub-selectors`: some useful redux selectors +* `xpub-ui`: a library of user interface elements for use in PubSweet components. + diff --git a/app/app.js b/app/app.js deleted file mode 100644 index 1f2aeaad23..0000000000 --- a/app/app.js +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import { AppContainer } from 'react-hot-loader' -import { browserHistory } from 'react-router' -import { syncHistoryWithStore } from 'react-router-redux' -import { configureStore, Root } from 'pubsweet-client' - -import './styles' - -let store = configureStore(browserHistory, {}) -let history = syncHistoryWithStore(browserHistory, store) - -const rootEl = document.getElementById('root') - -ReactDOM.render( - <AppContainer> - <Root store={store} history={history} /> - </AppContainer>, - rootEl -) - -if (module.hot) { - module.hot.accept('pubsweet-client/src/components/Root', () => { - const NextRoot = require('pubsweet-client/src/components/Root').default - - ReactDOM.render( - <AppContainer> - <NextRoot store={store} history={history} /> - </AppContainer>, - rootEl - ) - }) -} diff --git a/app/components/App.js b/app/components/App.js deleted file mode 100644 index d8de2cc1d5..0000000000 --- a/app/components/App.js +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import NavigationContainer from './NavigationContainer' - -const App = ({ children }) => ( - <div> - <NavigationContainer/> - <div style={{marginTop: 50}}> - {children} - </div> - </div> -) - -App.propTypes = { - children: PropTypes.node -} - -export default App diff --git a/app/components/Navigation.js b/app/components/Navigation.js deleted file mode 100644 index e6c4bf913b..0000000000 --- a/app/components/Navigation.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Nav, Navbar, NavbarBrand, NavItem } from 'react-bootstrap' - -const Navigation = ({ appLink, appName, logout, currentUser, updateSubscriber }) => ( - <Navbar fluid fixedTop style={{ minHeight: 0 }}> - <Navbar.Header> - <NavbarBrand> - <Navbar.Link href={appLink}>{appName}</Navbar.Link> - </NavbarBrand> - </Navbar.Header> - - {currentUser ? ( - <Nav pullRight> - {updateSubscriber && <NavItem>{updateSubscriber}</NavItem>} - <NavItem>logged in as {currentUser.username}</NavItem> - <NavItem onClick={logout}>logout</NavItem> - </Nav> - ) : ( - <Nav pullRight> - <NavItem> - <Navbar.Link href="/signin">login</Navbar.Link> - </NavItem> - </Nav> - )} - </Navbar> -) - -Navigation.propTypes = { - appLink: PropTypes.string.isRequired, - appName: PropTypes.string.isRequired, - currentUser: PropTypes.object, - logout: PropTypes.func.isRequired, - updateSubscriber: PropTypes.node -} - -export default Navigation diff --git a/app/components/NavigationContainer.js b/app/components/NavigationContainer.js deleted file mode 100644 index 729b694d8d..0000000000 --- a/app/components/NavigationContainer.js +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import UpdateSubscriber from 'pubsweet-client/src/components/UpdateSubscriber' -import { connect } from 'react-redux' -import { logoutUser } from 'pubsweet-component-login/actions' -import Navigation from './Navigation' - -const NavigationContainer = ({ logoutUser, currentUser }) => ( - <Navigation appName="xpub" // TODO: make configurable - appLink="/projects" // TODO: make configurable - currentUser={currentUser} - logout={logoutUser} - updateSubscriber={UpdateSubscriber}/> -) - -NavigationContainer.propTypes = { - currentUser: PropTypes.object, - logoutUser: PropTypes.func.isRequired -} - -const selectCurrentUser = (state) => state.currentUser.isAuthenticated - ? state.currentUser.user - : null - -export default connect( - state => ({ - currentUser: selectCurrentUser(state) - }), - { - logoutUser - } -)(NavigationContainer) diff --git a/app/components/WaxContainer.js b/app/components/WaxContainer.js deleted file mode 100644 index f85531fc43..0000000000 --- a/app/components/WaxContainer.js +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' -import { browserHistory } from 'react-router' -import { fileUpload } from 'pubsweet-client/src/actions/fileUpload' -import { getCollection } from 'pubsweet-client/src/actions/collections' -import { getFragment, updateFragment } from 'pubsweet-client/src/actions/fragments' -import SimpleEditor from 'pubsweet-component-wax/src/SimpleEditor' - -const fullscreenStyle = { - position: 'fixed', - top: 50, // leave room for the navbar - left: 0, - right: 0, - bottom: 0, - background: 'white', - overflow: 'hidden' -} - -class WaxContainer extends React.Component { - componentDidMount () { - const { getCollection, getFragment, params } = this.props - - getCollection({ id: params.project }) - getFragment({ id: params.project }, { id: params.version }) - } - - render () { - const { project, version, fileUpload, updateFragment, currentUser } = this.props - - if (!version || !project) return null - - return ( - <SimpleEditor - book={project} - fileUpload={fileUpload} - fragment={version} - history={browserHistory} - onSave={({ source }) => updateFragment(project, { id: version.id, source })} - update={data => updateFragment(project, { id: version.id, ...data })} - user={currentUser} - style={fullscreenStyle} - /> - ) - } -} - -WaxContainer.propTypes = { - fileUpload: PropTypes.func.isRequired, - getCollection: PropTypes.func.isRequired, - getFragment: PropTypes.func.isRequired, - updateFragment: PropTypes.func.isRequired, - currentUser: PropTypes.object, - params: PropTypes.object.isRequired, - project: PropTypes.object, - version: PropTypes.object -} - -const selectCollection = (state, id) => state.collections - .find(collection => collection.id === id) - -const selectCurrentUser = (state) => state.currentUser.isAuthenticated - ? state.currentUser.user - : null - -export default connect( - (state, ownProps) => ({ - currentUser: selectCurrentUser(state), - project: selectCollection(state, ownProps.params.project), - version: state.fragments[ownProps.params.version] - }), - { - fileUpload, - getCollection, - getFragment, - updateFragment - } -)(WaxContainer) diff --git a/app/routes.js b/app/routes.js deleted file mode 100644 index a2010fc666..0000000000 --- a/app/routes.js +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react' -import { IndexRoute, Redirect, Route } from 'react-router' - -import App from './components/app' -import AuthenticatedContainer from './components/AuthenticatedContainer' -import WaxContainer from './components/WaxContainer' - -import Signup from 'pubsweet-component-signup/Signup' -import Login from 'pubsweet-component-login/Login' -import PasswordReset from 'pubsweet-component-password-reset-frontend/PasswordReset' - -import ProjectListContainer from 'pubsweet-component-xpub-dashboard/components/ProjectListContainer' -import ProjectContainer from 'pubsweet-component-xpub-submission/components/ProjectContainer' -import VersionsListContainer from 'pubsweet-component-xpub-submission/components/VersionsListContainer' -import DeclarationsContainer from 'pubsweet-component-xpub-submission/components/DeclarationsContainer' - -export default ( - <Route> - <Redirect from="/" to="/projects"/> - - <Route path="/" component={App}> - <Route component={AuthenticatedContainer}> - <Route path="projects" component={ProjectListContainer}/> - - <Route path="projects/:project" component={ProjectContainer}> - <IndexRoute component={VersionsListContainer}/> - - <Route path="declarations" component={DeclarationsContainer}/> - </Route> - - <Route path="editor/:project/:version" component={WaxContainer}/> - </Route> - - <Route path="signup" component={Signup}/> - <Route path="login" component={Login}/> - <Route path="password-reset" component={PasswordReset}/> - </Route> - </Route> -) diff --git a/app/styles.js b/app/styles.js deleted file mode 100644 index 3d3a5ba984..0000000000 --- a/app/styles.js +++ /dev/null @@ -1,5 +0,0 @@ -import 'pubsweet-fira' -import 'typeface-fira-sans-condensed' -import 'typeface-vollkorn' - -import './styles/main.scss' diff --git a/app/styles/_base.scss b/app/styles/_base.scss deleted file mode 100644 index a21a82fc39..0000000000 --- a/app/styles/_base.scss +++ /dev/null @@ -1,26 +0,0 @@ -@import 'variables'; - -a { - color: $dark-grey; - text-decoration: underline; -} - -h1, -h2, -h3 { - color: $dark-grey; - margin-bottom: 5px; - margin-top: 5px; - padding-bottom: 0; - padding-top: 0; -} - -h2 { - font-size: 25px; - font-weight: bold; -} - -h3 { - color: lighten($dark-grey, 10%); - font-size: 20px; -} diff --git a/app/styles/_bootstrap.scss b/app/styles/_bootstrap.scss deleted file mode 100644 index 47f859b74c..0000000000 --- a/app/styles/_bootstrap.scss +++ /dev/null @@ -1,24 +0,0 @@ -@import 'variables'; - -// override bootstrap-sass default variables -$border-radius-base: 0; -$icon-font-path: '~bootstrap-sass/assets/fonts/bootstrap/'; - -@import '~bootstrap-sass/assets/stylesheets/bootstrap'; -@import '~bootstrap-sass/assets/stylesheets/bootstrap-sprockets'; - -@import 'base'; -@import 'bootstrap_buttons'; -@import 'bootstrap_form'; -@import 'navbar'; - -.alert.alert-warning { - background-color: $very-light-grey; - border: 0; - color: $black; - margin-top: 50px; -} - -.error { - color: $brown; -} diff --git a/app/styles/_bootstrap_buttons.scss b/app/styles/_bootstrap_buttons.scss deleted file mode 100644 index b2ba600896..0000000000 --- a/app/styles/_bootstrap_buttons.scss +++ /dev/null @@ -1,12 +0,0 @@ -@import 'variables'; - -.btn-primary { - background-color: $blue; - border-color: transparent; - - &:hover, - &:focus { - background-color: darken($blue, 10%); - border-color: darken($blue, 20%); - } -} diff --git a/app/styles/_bootstrap_form.scss b/app/styles/_bootstrap_form.scss deleted file mode 100644 index 5a9692e12c..0000000000 --- a/app/styles/_bootstrap_form.scss +++ /dev/null @@ -1,31 +0,0 @@ -@import 'variables'; - -legend { - border: 0; - color: $blue; - font-weight: bold; -} - -label { - color: $blue; - font-weight: normal; -} - -input[type='text'], -input[type='text'].form-control, -input[type='password'].form-control { - border: 2px solid $medium-light-gray; - - &.error { - border-right: 4px solid $main-grey; - } - - &.success { - border-right: 4px solid $dark-grey; - } -} - -.form-group { - margin-bottom: 15px; - margin-top: 15px; -} diff --git a/app/styles/_fonts.scss b/app/styles/_fonts.scss deleted file mode 100644 index 4e748c8352..0000000000 --- a/app/styles/_fonts.scss +++ /dev/null @@ -1,12 +0,0 @@ -//@import '~pubsweet-fira/src/fira'; -//@import '~typeface-fira-sans-condensed/index'; -//@import '~typeface-vollkorn/index'; - -.content-text { - font-family: 'Vollkorn', serif; -} - -.content-metadata, -.content-interactive { - font-family: 'Fira Sans Condensed', sans-serif; -} diff --git a/app/styles/_icons.scss b/app/styles/_icons.scss deleted file mode 100644 index cd0d29f878..0000000000 --- a/app/styles/_icons.scss +++ /dev/null @@ -1,2 +0,0 @@ -$fa-font-path: "~font-awesome/fonts"; -@import "~font-awesome/scss/font-awesome"; diff --git a/app/styles/_modal.scss b/app/styles/_modal.scss deleted file mode 100644 index eb51544a35..0000000000 --- a/app/styles/_modal.scss +++ /dev/null @@ -1,68 +0,0 @@ -$brown: #a52a2a; -$button-background: #d3d3d3; -$button-text: #000; -$grey: #808080; -$white: #fff; - -.modal-content { - border: 2px solid $grey; - border-radius: 0; - font-style: italic; - font-weight: 500; - padding: 1em; -} - -.modal-header { - padding: 0; -} - -.modal-title { - font-size: 24px; - line-height: 32px; -} - -.modal-body { - border: 0; - padding: 20px 0; -} - -.modal-footer { - border: 0; - font-style: normal; - font-weight: normal; - padding-right: 0; - text-align: center; -} - -.modal-buttons-container { - float: right; -} - -.modal-button { - background-color: $button-background; - border: 3px solid transparent; - border-radius: 3px; - color: $button-text; - display: inline-block; - margin-bottom: .5em; - padding: 4px 40px; - text-align: center; - text-decoration: none; - text-transform: uppercase; - - &:hover { - background-color: $grey; - color: $white; - cursor: pointer; - text-decoration: none; - } -} - -.modal-discard { - color: $brown; - margin-right: 20px; - - &:hover { - color: $brown; - } -} diff --git a/app/styles/_navbar.scss b/app/styles/_navbar.scss deleted file mode 100644 index cc27e0cdf8..0000000000 --- a/app/styles/_navbar.scss +++ /dev/null @@ -1,73 +0,0 @@ -@import 'variables'; - -@media (min-width: 768px) { - .navbar-brand { - margin-left: 0; - } -} - -.navbar-link, -.navbar-default .navbar-link { - color: #4990E2; - text-decoration: none; - font-weight: 800; -} - -.navbar-default { - border: 0; - font-weight: bold; - font-style: italic; - background-color: white; - - .container-fluid { - padding-left: 20px; - padding-right: 20px; - } - - .navbar-brand { - color: $blue; - height: auto; - padding: 0; - margin-top: 15px; - margin-left: 0 !important; - text-decoration: none; - display: inline-block; - - img { - height: 100%; - } - } - - .navbar-header { - font-style: italic; - } - - .navbar-nav { - margin-top: 0; - - > .active { - > a { - background-color: transparent; - color: #000; - font-weight: bold; - text-decoration: none; - - &:focus { - background-color: transparent; - } - - &:hover { - background-color: transparent; - } - } - } - - > li { - > a { - color: $blue; - font-style: italic; - text-decoration: none; - } - } - } -} diff --git a/app/styles/_reset.scss b/app/styles/_reset.scss deleted file mode 100644 index ef308c355e..0000000000 --- a/app/styles/_reset.scss +++ /dev/null @@ -1,136 +0,0 @@ -// http://meyerweb.com/eric/tools/css/reset/ -// v2.0 | 20110126 -// License: none (public domain) - -html, -body, -div, -span, -applet, -object, -iframe, -h1, -h2, -h3, -h4, -h5, -h6, -p, -blockquote, -pre, -a, -abbr, -acronym, -address, -big, -cite, -code, -del, -dfn, -em, -img, -ins, -kbd, -q, -s, -samp, -small, -strike, -strong, -sub, -sup, -tt, -var, -b, -u, -i, -center, -dl, -dt, -dd, -ol, -ul, -li, -fieldset, -form, -label, -legend, -table, -caption, -tbody, -tfoot, -thead, -tr, -th, -td, -article, -aside, -canvas, -details, -embed, -figure, -figcaption, -footer, -header, -hgroup, -menu, -nav, -output, -ruby, -section, -summary, -time, -mark, -audio, -video { - border: 0; - font: inherit; - font-size: 100%; - margin: 0; - padding: 0; - vertical-align: baseline; -} - - -// HTML5 display-role reset for older browsers - -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -menu, -nav, -section { - display: block; -} - -body { - line-height: 1; -} - -ol, -ul { - list-style: none; -} - -blockquote, -q { - quotes: none; -} - -blockquote::before, -blockquote::after, -q::before, -q::after { - // content: ''; - content: none; -} - -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/app/styles/_table.scss b/app/styles/_table.scss deleted file mode 100644 index 8f8c040f2d..0000000000 --- a/app/styles/_table.scss +++ /dev/null @@ -1,98 +0,0 @@ -@import 'variables'; - -.table { - margin-bottom: 10px; - - > thead { - > tr { - > th { - background-color: transparent; - border: 0; - color: $dark-grey; - text-align: left; - } - } - } - - > tbody { - > tr:first-child { - > td { - border: 0; - } - } - - > tr { - > td { - border: 0; - border-top: 1px $dark-grey solid; - padding-bottom: 20px; - padding-top: 20px; - } - } - } - - td.index, - th.index { - width: 5%; - } - - td.main, - th.main { - width: 50%; - } - - .btn-primary { - background-color: $main-grey; - border: 0; - margin-left: 5px; - - i { - margin-left: -2px; - margin-top: 2px; - } - } - - .btn-success { - background-color: #4B8AC9; - border: 0; - margin-left: 5px; - } - - .btn-danger { - background-color: $brown; - - border: 0; - margin-left: 5px; - - i { - margin-left: -1px; - } - } - - .btn-warning { - border: 0; - margin-left: 5px; - } - - .Select-value { - background-color: $main-grey; - border: 1px solid transparent; - color: $dark-grey; - - .Select-value-icon { - border-right: 1px solid $dark-grey; - - &:hover { - background-color: $medium-light-gray; - color: $white; - } - } - - } - - .Select-clear-zone { - &:hover { - color: $brown - } - } -} diff --git a/app/styles/_variables.scss b/app/styles/_variables.scss deleted file mode 100644 index ff845a0707..0000000000 --- a/app/styles/_variables.scss +++ /dev/null @@ -1,10 +0,0 @@ -$black: rgba(0, 0, 0, .75); -$brown: #a52a2a; -$white: #fff; -$blue: #4990e2; - -$dark-grey: #404040; -$light-grey: #cdcdcd; -$main-grey: #d8d8d8; -$medium-light-gray: #bab8b8; -$very-light-grey: #f2f2f2; diff --git a/app/styles/main.scss b/app/styles/main.scss deleted file mode 100644 index a6f5d8fe62..0000000000 --- a/app/styles/main.scss +++ /dev/null @@ -1,18 +0,0 @@ -@import 'reset'; -@import 'bootstrap'; -@import 'variables'; -@import 'fonts'; -@import 'icons'; - -html, -body { - color: $black; - font-family: 'Fira Sans', sans-serif; - line-height: 24px; - margin: 0; -} - -.separator { - clear: both; - height: 1px; -} diff --git a/config/shared.js b/config/shared.js deleted file mode 100644 index 2261e133de..0000000000 --- a/config/shared.js +++ /dev/null @@ -1,66 +0,0 @@ -const path = require('path') -const Joi = require('joi') -const permissions = require('../permissions/index') - -module.exports = { - 'pubsweet-server': { - dbPath: path.join(__dirname, '..', 'api', 'db'), - API_ENDPOINT: 'http://localhost:3000/api' - }, - 'pubsweet-client': { - 'login-redirect': '/', - theme: process.env.PUBSWEET_THEME - }, - 'mail-transport': { - sendmail: true - }, - 'password-reset': { - url: process.env.PUBSWEET_PASSWORD_RESET_URL || 'http://localhost:3000/password-reset', - sender: process.env.PUBSWEET_PASSWORD_RESET_SENDER || 'dev@example.com' - }, - authsome: { - mode: permissions, - teams: { - // TODO - } - }, - pubsweet: { - components: require('./components.json') - }, - 'pubsweet-component-ink-backend': { - inkEndpoint: process.env.INK_ENDPOINT || 'http://ink-api.coko.foundation', - email: process.env.INK_USERNAME, - password: process.env.INK_PASSWORD, - maxRetries: 500 - }, - validations: { - collection: { // project - declarations: Joi.object(), - events: Joi.object(), - roles: Joi.object(), - status: Joi.string().required(), // TODO: use the latest workflow event? - statusDate: Joi.date().timestamp().required(), // TODO: use the latest workflow event? - title: Joi.string().required() - }, - fragment: { // version - comments: Joi.object(), // wax - declarations: Joi.object(), - events: Joi.object(), - files: Joi.object(), - lock: Joi.object().allow(null), // wax - metadata: Joi.object(), - progress: Joi.object(), // wax - published: Joi.date().timestamp(), - roles: Joi.object(), - source: Joi.string().required(), // wax (TODO: move to file) - status: Joi.string(), - submitted: Joi.date().timestamp(), - trackChanges: Joi.boolean(), // wax - version: Joi.number().required() - }, - user: { - name: Joi.string(), // TODO: add "name" to the login form - editor: Joi.boolean() - } - } -} diff --git a/docs/schema-outline.ftml b/docs/schema-outline.ftml deleted file mode 100644 index 04b937b4d2..0000000000 --- a/docs/schema-outline.ftml +++ /dev/null @@ -1,419 +0,0 @@ -<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta name="expandedItems" content="X1GX6h6Cm X1xlTqp07 mJ24-iaRX X1kNqp6C7 QJo4qpp07 mJkGp3aRQ QJXEp5aA7 Qy2j-sTCQ X1j5caTRX Xyi2566CQ X1r-Tn60m mykgbs6C7 mkmksTTAm XJqCtpaRm XkmY5a6AX QkCDUAaCm QJv3boaAm myvUFC6C7 Q1N8FRTCm mkFWf1CAX mkGzz1CC7 Q1d1wThaCm X1opC2pR7 mkAe36TC7 Xy6TAnT0Q" /> - <meta charset="UTF-8" /> - </head> - <body> - <ul id="FoldingText"> - <li id="X1GX6h6Cm"> - <p>journals</p> - <ul> - <li id="X1xlTqp07"> - <p><b>Journal</b></p> - <ul> - <li id="QknM6qaCQ"> - <p>metadata</p> - <ul> - <li id="QyLu-opA7"> - <p>title</p> - </li> - <li id="my_dbiaA7"> - <p>issn</p> - </li> - <li id="XkltZi6CQ"> - <p>url</p> - </li> - </ul> - </li> - <li id="XkMKZoT0m"> - <p>defaults</p> - <ul> - <li id="Q1rqWiTR7"> - <p>review deadline</p> - </li> - </ul> - </li> - <li id="mJ24-iaRX"> - <p>roles</p> - <ul> - <li id="XkfsWjTRX"> - <p>staff</p> - </li> - <li id="XyUsbipCQ"> - <p>editor</p> - </li> - </ul> - </li> - <li id="X1kNqp6C7"> - <p>declarations</p> - <ul> - <li id="QJo4qpp07"> - <p><b>Declaration Question</b></p> - <ul> - <li id="QyySc66RX"> - <p>key</p> - </li> - <li id="Q1irqTp0Q"> - <p>question UI component</p> - </li> - <li id="QJPP9TpRQ"> - <p>answer UI component</p> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </li> - <li id="mJkGp3aRQ"> - <p>projects</p> - <ul> - <li id="QJXEp5aA7"> - <p><b>Project</b></p> - <ul> - <li id="X1ZEh6TR7"> - <p>title</p> - </li> - <li id="Q19r6qpRX"> - <p>roles</p> - <ul> - <li id="Qy2j-sTCQ"> - <p>owner</p> - <ul> - <li id="X1j5caTRX"> - <p><b>Role</b></p> - <ul> - <li id="XkJjcTaAX"> - <p>type</p> - </li> - <li id="XksXoapCX"> - <p>user</p> - </li> - <li id="Xyi2566CQ"> - <p>events</p> - <ul> - <li id="XkhgiTaRm"> - <p>started</p> - </li> - <li id="myCgsp6A7"> - <p>ended</p> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </li> - <li id="Qypi-jpAQ"> - <p>editor</p> - </li> - <li id="myJnZspCQ"> - <p>reviewer</p> - </li> - </ul> - </li> - <li id="XkMDp560X"> - <p>events</p> - <ul> - <li id="XJylGipCm"> - <p>created</p> - </li> - <li id="Xy7xGop0X"> - <p>title changed</p> - </li> - <li id="XkFeGoaA7"> - <p>role added/removed</p> - </li> - </ul> - </li> - <li id="X1r-Tn60m"> - <p>versions</p> - <ul> - <li id="mykgbs6C7"> - <p><b>Version</b></p> - <ul> - <li id="XkkksT60X"> - <p>files</p> - <ul> - <li id="mkmksTTAm"> - <p><b>File</b></p> - <ul> - <li id="Q1wU1zk1V"> - <p>type (e.g. "manuscript", "figure")</p> - </li> - <li id="mknwkG11V"> - <p>key</p> - </li> - <li id="my-wyz1kN"> - <p>blob id</p> - </li> - <li id="Q1J0qaTAX"> - <p>filename</p> - </li> - <li id="QkgC9TTR7"> - <p>title</p> - </li> - <li id="mkZR9pa0X"> - <p>size</p> - </li> - <li id="XJzCca60X"> - <p>MIME type</p> - </li> - <li id="QymAc66R7"> - <p>description</p> - </li> - <li id="XJqCtpaRm"> - <p>events</p> - <ul> - <li id="Q1nRF660m"> - <p>added</p> - </li> - <li id="myybcp6CX"> - <p>updated</p> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </li> - <li id="myRzMsTRX"> - <p>declarations</p> - <ul> - <li id="XkmY5a6AX"> - <p><b>Declaration Answer</b></p> - <ul> - <li id="QkhYqp6R7"> - <p>question key</p> - </li> - <li id="QkmvLRTR7"> - <p>question text?</p> - </li> - <li id="mJ0Y5ap0m"> - <p>answer</p> - </li> - <li id="QkCDUAaCm"> - <p>events</p> - <ul> - <li id="X1Zd8CTA7"> - <p>answered</p> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </li> - <li id="X14_op6RQ"> - <p>metadata</p> - <ul> - <li id="myBB2pa0Q"> - <p>title (with markup)</p> - </li> - <li id="X19_opp0m"> - <p>authors/contributors</p> - </li> - <li id="mJsuoTaAQ"> - <p>affiliations</p> - </li> - <li id="XJCdsTpCm"> - <p>funders</p> - </li> - <li id="XJ7Ks66AQ"> - <p>contributions</p> - </li> - </ul> - </li> - <li id="mykfZiaC7"> - <p>roles</p> - <ul> - <li id="QJv3boaAm"> - <p>reviewer</p> - <ul> - <li id="myvUFC6C7"> - <p><b>Role</b></p> - <ul> - <li id="QJZA-ja0X"> - <p>invitation</p> - </li> - <li id="QJGzYApR7"> - <p>project role id</p> - </li> - <li id="Q1N8FRTCm"> - <p>events</p> - <ul> - <li id="Xk2IFA6RQ"> - <p>invited</p> - </li> - <li id="XJA8KApRQ"> - <p>accepted</p> - </li> - <li id="QyxPFCTR7"> - <p>declined</p> - </li> - <li id="QJUwF0aCm"> - <p>submitted review</p> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </li> - <li id="XyiMWjaRm"> - <p>notes/messages/tasks</p> - <ul> - <li id="mkFWf1CAX"> - <p><b>Note</b></p> - <ul> - <li id="mkGzz1CC7"> - <p>type</p> - <ul> - <li id="QypTopa07"> - <p>changes needed</p> - </li> - <li id="mkH0ip6Am"> - <p>notes for the editor</p> - </li> - <li id="Xyl1h6a0m"> - <p>notes for the reviewers</p> - </li> - <li id="QkKNpT6CX"> - <p>review</p> - </li> - <li id="m1jE66pA7"> - <p>decision</p> - </li> - <li id="mynNTpaC7"> - <p>author response</p> - </li> - </ul> - </li> - <li id="QkPfzk00m"> - <p>attachments (or inline links to uploaded files?)</p> - </li> - </ul> - </li> - </ul> - </li> - <li id="Q1d1wThaCm"> - <p>events</p> - <ul> - <li id="XkJwahaAQ"> - <p>created</p> - </li> - <li id="m1kC1Gyy4"> - <p>file added/removed/updated</p> - </li> - <li id="XkG1J100Q"> - <p>declarations saved/updated</p> - </li> - <li id="Q11myJ0AX"> - <p>submitted for preprint</p> - </li> - <li id="QJVkDa2pCm"> - <p>submitted for peer review</p> - </li> - <li id="XyM2Fpa07"> - <p>checked</p> - </li> - <li id="XJ31k1RA7"> - <p>editor assigned</p> - </li> - <li id="QyyeykAR7"> - <p>reviewers invited</p> - </li> - <li id="X17xyJR0Q"> - <p>reviewers accepted</p> - </li> - <li id="m1leMkA0m"> - <p>reviews submitted</p> - </li> - <li id="XyVgz1AA7"> - <p>decision sent</p> - </li> - <li id="mkdxM1CCm"> - <p>author response received</p> - </li> - <li id="QkHyvTn6RQ"> - <p>metadata updated</p> - </li> - <li id="myvyw62TCQ"> - <p>preprint approved</p> - </li> - <li id="Qk8yPp3a07"> - <p>publication approved</p> - </li> - <li id="XJ3SG1ACX"> - <p>published</p> - </li> - </ul> - </li> - <li id="XkE0p3T0m"> - <p>links</p> - <ul> - <li id="QyDAT2aCm"> - <p>previous published version</p> - </li> - <li id="QysRpnaCQ"> - <p>next published version</p> - </li> - <li id="mkTCph6RQ"> - <p>latest published version</p> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </li> - <li id="X1opC2pR7"> - <p>users</p> - <ul> - <li id="mk-4jTTRm"> - <p>name (given + family)</p> - </li> - <li id="Qy4EiaT0X"> - <p>email address</p> - </li> - <li id="mkAe36TC7"> - <p>identifiers</p> - <ul> - <li id="mk8WnaaCX"> - <p>ORCID</p> - </li> - <li id="QJ5Wha6Cm"> - <p>Twitter</p> - </li> - <li id="m1yzhTpAQ"> - <p>Google Scholar</p> - </li> - <li id="mkMMhppAm"> - <p>URL</p> - </li> - </ul> - </li> - <li id="Xy6TAnT0Q"> - <p>roles (on journal, project or version)</p> - <ul> - <li id="myg0RnpA7"> - <p>staff</p> - </li> - <li id="Q1M00nT07"> - <p>editor</p> - </li> - </ul> - </li> - </ul> - </li> - </ul> - </body> -</html> diff --git a/docs/workflow-outline.ftml b/docs/workflow-outline.ftml deleted file mode 100644 index d74b165db4..0000000000 --- a/docs/workflow-outline.ftml +++ /dev/null @@ -1,121 +0,0 @@ -<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"> - <head> - <meta name="expandedItems" content="mJPQMgARQ QkRBMlCA7 QJqIzeRAX" /> - <meta charset="UTF-8" /> - </head> - <body> - <ul id="FoldingText"> - <li id="mJPQMgARQ"> - <p>create project</p> - <ul> - <li id="XyWXf21RRQ"> - <p>upload manuscript</p> - </li> - <li id="QJo7zeC0m"> - <p>extract title</p> - </li> - </ul> - </li> - <li id="mJmDfxCAQ"> - <p>edit title</p> - </li> - <li id="Q1gNGxCR7"> - <p>edit manuscript</p> - </li> - <li id="QJzXBxAAm"> - <p>upload supporting files</p> - </li> - <li id="QkRBMlCA7"> - <p>submit project for peer review</p> - <ul> - <li id="XkHIMeCC7"> - <p>answer declarations</p> - </li> - <li id="Xk4OMg0R7"> - <p>accept terms and conditions</p> - </li> - <li id="XJ6FGgA0m"> - <p>create version</p> - </li> - <li id="Qkgf5XlACX"> - <p>check manuscript</p> - </li> - <li id="m1WG5QxCCX"> - <p>check declarations</p> - </li> - <li id="QJTMBlRC7"> - <p>check files</p> - </li> - <li id="myOpmeACQ"> - <p>assign editor to project</p> - </li> - <li id="mJjpQxRCX"> - <p>assign reviewers to project</p> - </li> - <li id="mkxRQe0CQ"> - <p>invite reviewers to version</p> - </li> - <li id="Qy3AQxC0m"> - <p>accept/decline reviewer invitation</p> - </li> - <li id="QkGkVg0Cm"> - <p>submit reviews</p> - </li> - <li id="X1T1EeRCm"> - <p>send decision</p> - </li> - <li id="XyxxNeRCX"> - <p>submit author response</p> - </li> - <li id="X1Lrre007"> - <p>[repeat if necessary]</p> - </li> - <li id="myBLSeRAX"> - <p>upload high-resolution figures</p> - </li> - <li id="XyMOSx007"> - <p>check figure files</p> - </li> - <li id="mJzGqQx0RX"> - <p>extract metadata from manuscript</p> - </li> - <li id="QJXfcXlR07"> - <p>check/enter/correct metadata</p> - </li> - <li id="QkH2170A7"> - <p>publish</p> - </li> - </ul> - </li> - <li id="QJqIzeRAX"> - <p>submit project for preprint</p> - <ul> - <li id="Xy-qGgAC7"> - <p>answer declarations</p> - </li> - <li id="XJU5MeRCX"> - <p>accept terms and conditions</p> - </li> - <li id="mJ55zlA0X"> - <p>create version</p> - </li> - <li id="Qy12MlA0m"> - <p>check manuscript</p> - </li> - <li id="X1dCfxRRm"> - <p>check declarations</p> - </li> - <li id="XJj0fxC0m"> - <p>extract metadata from manuscript</p> - </li> - <li id="XyklQg0A7"> - <p>check/enter/correct metadata</p> - </li> - <li id="XkDqme0A7"> - <p>publish preprint</p> - </li> - </ul> - </li> - </ul> - </body> -</html> \ No newline at end of file diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000000..8ac1b113ec --- /dev/null +++ b/lerna.json @@ -0,0 +1,7 @@ +{ + "lerna": "2.0.0", + "version": "independent", + "packages": [ + "packages/*" + ] +} diff --git a/package.json b/package.json index 4675067b98..652c268a0b 100644 --- a/package.json +++ b/package.json @@ -1,82 +1,28 @@ { "name": "xpub", - "version": "0.0.1", - "description": "xpub", + "version": "0.0.0", + "private": true, "license": "MIT", - "repository": { - "type": "git", - "url": "https://gitlab.coko.foundation/xpub/xpub" - }, - "dependencies": { - "pubsweet": "1.0.0-alpha.4", - "font-awesome": "^4.7.0", - "joi": "^10.4.1", - "lodash": "^4.17.4", - "moment": "^2.18.1", - "prop-types": "^15.5.10", - "pubsweet-client": "^1.0.0-alpha.1", - "pubsweet-component-ink-backend": "0.0.10", - "pubsweet-component-ink-frontend": "^0.1.0", - "pubsweet-component-login": "^0.3.0", - "pubsweet-component-password-reset-backend": "^0.1.0", - "pubsweet-component-password-reset-frontend": "^0.1.0", - "pubsweet-component-signup": "^0.2.0", - "pubsweet-component-wax": "0.1.0", - "pubsweet-component-xpub-dashboard": "*", - "pubsweet-component-xpub-submission": "*", - "pubsweet-fira": "^0.0.3", - "pubsweet-server": "^1.0.0-alpha.1", - "pubsweet-theme-plugin": "^0.0.1", - "react": "^15.4.4", - "react-bootstrap": "^0.31.0", - "react-dom": "^15.5.4", - "react-loadable": "^4.0.3", - "react-redux": "^5.0.2", - "react-router": "^3.0.5", - "react-router-bootstrap": "^0.23.3", - "react-router-redux": "^4.0.7", - "redux": "^3.6.0", - "redux-logger": "^3.0.1", - "typeface-fira-sans-condensed": "0.0.31", - "typeface-vollkorn": "0.0.31" - }, "devDependencies": { - "app-module-path": "^2.2.0", - "babel-core": "^6.14.0", "babel-eslint": "^7.2.3", - "babel-loader": "^7.0.0", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-preset-babili": "0.1.4", - "babel-preset-env": "^1.6.0", - "babel-preset-react": "^6.11.1", - "babel-preset-stage-2": "^6.13.0", - "babili-webpack-plugin": "^0.1.2", - "bootstrap-sass": "^3.3.7", - "compression-webpack-plugin": "^0.4.0", - "copy-webpack-plugin": "^4.0.1", - "css-loader": "^0.28.1", - "eslint": "^3.19.0", - "eslint-loader": "^1.6.0", - "extract-text-webpack-plugin": "^2.0.0-beta.4", - "file-loader": "^0.11.1", - "html-webpack-plugin": "^2.24.0", - "joi-browser": "^10.0.6", - "json-loader": "^0.5.4", - "node-sass": "^4.5.2", - "pouchdb-adapter-memory": "^6.1.1", - "react-hot-loader": "^3.0.0-beta.6", - "sass-loader": "^6.0.3", - "script-loader": "^0.7.0", - "standard": "^10.0.2", - "string-replace-loader": "^1.3.0", - "style-loader": "^0.18.2", - "url-loader": "^0.5.8", - "webpack": "^2.6.1", - "webpack-dev-middleware": "^1.10.2", - "webpack-hot-middleware": "^2.18.1" + "eslint": "^4.4.1", + "eslint-config-react-app": "^2.0.0", + "eslint-plugin-flowtype": "^2.34.1", + "eslint-plugin-import": "^2.6.0", + "eslint-plugin-jsx-a11y": "^5.1.1", + "eslint-plugin-react": "^7.1.0", + "lerna": "^2.0.0" + }, + "repository": { + "type": "git", + "url": "git@gitlab.coko.foundation:xpub/xpub.git" }, "scripts": { - "setupdb": "pubsweet setupdb --dev ./", - "start": "pubsweet run --dev" + "bootstrap": "lerna bootstrap --concurrency=1", + "hoist": "lerna bootstrap --hoist --concurrency=1", + "clean": "lerna clean" + }, + "engines": { + "node": ">=7.9" } } diff --git a/packages/collabra/.gitignore b/packages/collabra/.gitignore new file mode 100644 index 0000000000..1468569887 --- /dev/null +++ b/packages/collabra/.gitignore @@ -0,0 +1,6 @@ +_build/ +api/ +logs/ +node_modules/ +.env.* +.env diff --git a/packages/collabra/app/app.js b/packages/collabra/app/app.js new file mode 100644 index 0000000000..b0d7270919 --- /dev/null +++ b/packages/collabra/app/app.js @@ -0,0 +1,33 @@ +import React from 'react' +import ReactDOM from 'react-dom' +import { Provider } from 'react-redux' +import { AppContainer } from 'react-hot-loader' +import { Router, browserHistory } from 'react-router' +import { syncHistoryWithStore } from 'react-router-redux' +import { configureStore } from 'pubsweet-client' +import routes from './routes' +import 'xpub-fonts' + +const store = configureStore(browserHistory, {}) +const history = syncHistoryWithStore(browserHistory, store) + +const render = routes => { + ReactDOM.render( + <AppContainer> + <Provider store={store}> + <Router history={history}> + {routes} + </Router> + </Provider> + </AppContainer>, + document.getElementById('root') + ) +} + +render(routes) + +if (module.hot) { + module.hot.accept('./routes', () => { + render(routes) + }) +} diff --git a/packages/collabra/app/components/App.js b/packages/collabra/app/components/App.js new file mode 100644 index 0000000000..c6fc2af467 --- /dev/null +++ b/packages/collabra/app/components/App.js @@ -0,0 +1,77 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { compose, withContext } from 'recompose' +import { connect } from 'react-redux' +import { AppBar } from 'xpub-ui' +import { selectCurrentUser } from 'xpub-selectors' +import { actions } from 'pubsweet-client' +import classes from './App.local.css' +import * as journal from '../config/journal' + +// NOTE: currently loading all collections and fragments into the store on startup! +// TODO: dispatch loading actions from components, via HOC + +class App extends React.Component { + componentDidMount () { + const { getProjects, getVersions } = this.props + + getProjects().then(({ collections: projects }) => { + projects.forEach(project => getVersions(project)) + }) + } + + render () { + const { children, currentUser } = this.props + + return ( + <div className={classes.root}> + <AppBar + appName={journal.metadata.name} + appLink="/projects" // TODO: make configurable + userName={currentUser ? currentUser.username : null} + loginLink="/signin" + logoutLink="/signout"/> + + <div className={classes.main}> + {children} + </div> + </div> + ) + } +} + +/*const App = ({ children, currentUser }) => ( + <div className={classes.root}> + <AppBar + appName={journal.metadata.name} + appLink="/projects" // TODO: make configurable + userName={currentUser ? currentUser.username : null} + loginLink="/signin" + logoutLink="/signout"/> + + <div className={classes.main}> + {children} + </div> + </div> +)*/ + +App.propTypes = { + children: PropTypes.node, + currentUser: PropTypes.object +} + +export default compose( + connect( + state => ({ + currentUser: selectCurrentUser(state) + }), + { + getProjects: actions.getCollections, + getVersions: actions.getFragments + } + ), + withContext( + { journal: PropTypes.object }, + () => ({ journal }) + ), +)(App) diff --git a/packages/collabra/app/components/App.local.css b/packages/collabra/app/components/App.local.css new file mode 100644 index 0000000000..d15b54fd71 --- /dev/null +++ b/packages/collabra/app/components/App.local.css @@ -0,0 +1,3 @@ +.main { + margin-top: 50px; +} diff --git a/app/components/AuthenticatedContainer.js b/packages/collabra/app/components/AuthenticatedPage.js similarity index 69% rename from app/components/AuthenticatedContainer.js rename to packages/collabra/app/components/AuthenticatedPage.js index a9d210e9b1..d7446d1884 100644 --- a/app/components/AuthenticatedContainer.js +++ b/packages/collabra/app/components/AuthenticatedPage.js @@ -1,11 +1,12 @@ import React from 'react' import PropTypes from 'prop-types' +import { compose } from 'recompose' import { connect } from 'react-redux' import { push } from 'react-router-redux' -import { getCurrentUser } from 'pubsweet-client/src/actions/currentUser' +import { actions } from 'pubsweet-client' import { withRouter } from 'react-router' -class AuthenticatedContainer extends React.Component { +class AuthenticatedPage extends React.Component { componentDidMount () { const { isAuthenticated, getCurrentUser } = this.props @@ -35,7 +36,7 @@ class AuthenticatedContainer extends React.Component { } } -AuthenticatedContainer.propTypes = { +AuthenticatedPage.propTypes = { children: PropTypes.node.isRequired, getCurrentUser: PropTypes.func.isRequired, isAuthenticated: PropTypes.bool.isRequired, @@ -44,13 +45,16 @@ AuthenticatedContainer.propTypes = { push: PropTypes.func.isRequired } -export default withRouter(connect( - state => ({ - isAuthenticated: state.currentUser.isAuthenticated, - isFetching: state.currentUser.isFetching - }), - { - getCurrentUser, - push - } -)(AuthenticatedContainer)) +export default compose( + connect( + state => ({ + isAuthenticated: state.currentUser.isAuthenticated, + isFetching: state.currentUser.isFetching + }), + { + getCurrentUser: actions.getCurrentUser, + push + } + ), + withRouter +)(AuthenticatedPage) diff --git a/packages/collabra/app/components/Logout.js b/packages/collabra/app/components/Logout.js new file mode 100644 index 0000000000..64d48f0c87 --- /dev/null +++ b/packages/collabra/app/components/Logout.js @@ -0,0 +1,37 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { compose } from 'recompose' +import { connect } from 'react-redux' +import { logoutUser } from 'pubsweet-component-login/actions' + +class Logout extends React.Component { + componentDidMount () { + const { isAuthenticated, logoutUser } = this.props + + if (isAuthenticated) { + logoutUser() + } + } + + render () { + const { isAuthenticated } = this.props + + return isAuthenticated ? 'Signed out' : 'Signing out…' + } +} + +Logout.propTypes = { + logoutUser: PropTypes.func.isRequired, + isAuthenticated: PropTypes.bool.isRequired, +} + +export default compose( + connect( + state => ({ + isAuthenticated: state.currentUser.isAuthenticated, + }), + { + logoutUser + } + ), +)(Logout) diff --git a/packages/collabra/app/components/SubmitPage.js b/packages/collabra/app/components/SubmitPage.js new file mode 100644 index 0000000000..1decd05384 --- /dev/null +++ b/packages/collabra/app/components/SubmitPage.js @@ -0,0 +1,18 @@ +import { compose } from 'recompose' +import { connect } from 'redux' +import { actions } from 'pubsweet-client' +import { selectCollection } from 'xpub-selectors' + +import Submit from 'pubsweet-component-xpub-submit' + +export default compose( + connect( + (state, ownProps) => ({ + project: selectCollection(state, ownProps.params.project) + }), + { + getVersions: actions.getFragments, + getProject: actions.getCollection, + } + ) +)(Submit) diff --git a/packages/collabra/app/config/journal/decisions.js b/packages/collabra/app/config/journal/decisions.js new file mode 100644 index 0000000000..7b9e6dabf6 --- /dev/null +++ b/packages/collabra/app/config/journal/decisions.js @@ -0,0 +1,22 @@ +export default { + accept: { + label: 'Accept', + color: 'green', + message: 'The submission has been accepted for publication' + }, + minor: { + label: 'Minor revisions', + color: 'orange', + message: 'The submission will be accepted for publication after minor changes' + }, + major: { + label: 'Major revisions', + color: 'yellow', + message: 'The requested changes must be made before submission for review' + }, + reject: { + label: 'Reject', + color: 'red', + message: 'The submission is not acceptable for publication' + }, +} diff --git a/packages/collabra/app/config/journal/declarations.js b/packages/collabra/app/config/journal/declarations.js new file mode 100644 index 0000000000..6c7ea1741f --- /dev/null +++ b/packages/collabra/app/config/journal/declarations.js @@ -0,0 +1,81 @@ +import React from 'react' + +export default { + valid: model => !!model.accept, // TODO: use form validation instead? + sections: [ + { + name: 'Ethics', + items: [ + { + name: 'human', + type: 'checkbox', + label: 'This research included study of human participants or human tissue', + render: value => ( + <div>This research {value ? 'included' : 'did not include'} study of human participants or human tissue.</div> + ) + }, + { + name: 'humanReview', + type: 'text', + // label: 'Institutional review board', + helperText: 'Please name the Institutional Review Board which approved this research', + condition: model => model.human, + required: true, + render: value => ( + <div> + <div>The Institutional Review Board which approved this research:</div> + <div>{value}</div> + </div> + ) + } + ] + }, + { + name: 'Influence', + items: [ + { + name: 'financialDisclosure', + type: 'text', + // label: 'Funding disclosure', + helperText: 'Please disclose any role that the funders played in your research and/or manuscript', + render: value => ( + <div> + <div>Roles that the funders played in the research and/or manuscript:</div> + <div>{value}</div> + </div> + ) + } + ] + }, + { + name: 'Discoveries', + items: [ + { + name: 'newTaxon', + type: 'checkbox', + label: 'This paper describes a new taxon', + content: value => ( + value && <div>Please review <a href="https://submit.elifesciences.org/html/elife_author_instructions.html" target="_blank" rel="noopener noreferrer" style={{textDecoration:'underline'}}>our policies on new taxon nomenclature.</a></div> + ), + render: value => ( + <div>This research {value ? 'describes' : 'does not describe'} a new taxon.</div> + ) + } + ] + }, + { + name: 'Terms & Conditions', + items: [ + { + name: 'accept', + type: 'checkbox', + required: true, + label: 'By checking this box, I accept the terms and conditions', // TODO: link + render: value => ( + <div>The terms and conditions {value ? 'were' : 'were not'} accepted.</div> + ) + } + ] + }, + ] +} diff --git a/packages/collabra/app/config/journal/index.js b/packages/collabra/app/config/journal/index.js new file mode 100644 index 0000000000..62d90b4a11 --- /dev/null +++ b/packages/collabra/app/config/journal/index.js @@ -0,0 +1,4 @@ +export { default as metadata } from './metadata' +export { default as declarations } from './declarations' +export { default as decisions } from './decisions' +export { default as sections } from './sections' diff --git a/packages/collabra/app/config/journal/metadata.js b/packages/collabra/app/config/journal/metadata.js new file mode 100644 index 0000000000..0040ca7d9e --- /dev/null +++ b/packages/collabra/app/config/journal/metadata.js @@ -0,0 +1,4 @@ +export default { + name: 'Collabra: Psychology', + issn: '2474-7394' +} diff --git a/packages/collabra/app/config/journal/sections.js b/packages/collabra/app/config/journal/sections.js new file mode 100644 index 0000000000..419171898b --- /dev/null +++ b/packages/collabra/app/config/journal/sections.js @@ -0,0 +1,6 @@ +export default [ + { + id: 'submissions', + label: 'My Submissions' + } +] diff --git a/app/index.ejs b/packages/collabra/app/index.ejs similarity index 100% rename from app/index.ejs rename to packages/collabra/app/index.ejs diff --git a/app/index.html b/packages/collabra/app/index.html similarity index 100% rename from app/index.html rename to packages/collabra/app/index.html diff --git a/packages/collabra/app/routes.js b/packages/collabra/app/routes.js new file mode 100644 index 0000000000..0642f98adf --- /dev/null +++ b/packages/collabra/app/routes.js @@ -0,0 +1,37 @@ +import React from 'react' +import { Redirect, Route } from 'react-router' +// import loadable from 'loadable-components' + +import App from './components/app' +import AuthenticatedPage from './components/AuthenticatedPage' + +import Signup from 'pubsweet-component-signup/Signup' +import Login from 'pubsweet-component-login/Login' +import Logout from './components/Logout' +import PasswordReset from 'pubsweet-component-password-reset-frontend/PasswordReset' + +import DashboardPage from 'pubsweet-component-xpub-dashboard/src/components' + +import SubmitPage from 'pubsweet-component-xpub-submit/src/components' + +import ManuscriptPage from 'pubsweet-component-xpub-manuscript/src/components' +// const ManuscriptPage = loadable(() => import('pubsweet-component-xpub-manuscript/src/components')) + +export default ( + <Route> + <Redirect from="/" to="/projects"/> + + <Route path="/" component={App}> + <Route component={AuthenticatedPage}> + <Route path="dashboard" component={DashboardPage}/> + <Route path="projects/:project/submit" component={SubmitPage}/> + <Route path="projects/:project/manuscript" component={ManuscriptPage}/> + </Route> + + <Route path="signup" component={Signup}/> + <Route path="login" component={Login}/> + <Route path="logout" component={Logout}/> + <Route path="password-reset" component={PasswordReset}/> + </Route> + </Route> +) diff --git a/packages/collabra/config/authsome.js b/packages/collabra/config/authsome.js new file mode 100644 index 0000000000..5d6ee0dc29 --- /dev/null +++ b/packages/collabra/config/authsome.js @@ -0,0 +1,8 @@ +module.exports = { + mode: (user, operation, project, version) => { + return true // TODO + }, + teams: { + // TODO + } +} diff --git a/config/components.json b/packages/collabra/config/components.json similarity index 58% rename from config/components.json rename to packages/collabra/config/components.json index bf2d69f628..4e0f0b2f82 100644 --- a/config/components.json +++ b/packages/collabra/config/components.json @@ -4,8 +4,5 @@ "pubsweet-component-password-reset-frontend", "pubsweet-component-password-reset-backend", "pubsweet-component-ink-frontend", - "pubsweet-component-ink-backend", - "pubsweet-component-wax", - "pubsweet-component-xpub-dashboard", - "pubsweet-component-xpub-submission" + "pubsweet-component-ink-backend" ] diff --git a/config/dev.js b/packages/collabra/config/dev.js similarity index 100% rename from config/dev.js rename to packages/collabra/config/dev.js diff --git a/config/development.js b/packages/collabra/config/development.js similarity index 100% rename from config/development.js rename to packages/collabra/config/development.js diff --git a/config/production.js b/packages/collabra/config/production.js similarity index 100% rename from config/production.js rename to packages/collabra/config/production.js diff --git a/packages/collabra/config/shared.js b/packages/collabra/config/shared.js new file mode 100644 index 0000000000..ffa8fa26de --- /dev/null +++ b/packages/collabra/config/shared.js @@ -0,0 +1,32 @@ +const authsome = require('./authsome') +const components = require('./components.json') +const validations = require('./validations') + +module.exports = { + authsome, + validations, + pubsweet: { + components + }, + 'pubsweet-server': { + dbPath: 'http://localhost:5984/', + API_ENDPOINT: 'http://localhost:3000/api' + }, + 'pubsweet-client': { + 'login-redirect': '/', + theme: process.env.PUBSWEET_THEME + }, + 'mail-transport': { + sendmail: true + }, + 'password-reset': { + url: process.env.PUBSWEET_PASSWORD_RESET_URL || 'http://localhost:3000/password-reset', + sender: process.env.PUBSWEET_PASSWORD_RESET_SENDER || 'dev@example.com' + }, + 'pubsweet-component-ink-backend': { + inkEndpoint: process.env.INK_ENDPOINT || 'http://ink-api.coko.foundation', + email: process.env.INK_USERNAME, + password: process.env.INK_PASSWORD, + maxRetries: 500 + }, +} diff --git a/config/test.js b/packages/collabra/config/test.js similarity index 100% rename from config/test.js rename to packages/collabra/config/test.js diff --git a/packages/collabra/config/validations.js b/packages/collabra/config/validations.js new file mode 100644 index 0000000000..fdf4839e8e --- /dev/null +++ b/packages/collabra/config/validations.js @@ -0,0 +1,15 @@ +const Joi = require('joi') + +module.exports = { + collection: { // project + }, + fragment: { // version + version: Joi.number().required(), + title: Joi.string(), + abstract: Joi.string(), + authors: Joi.string(), + }, + user: { + name: Joi.string(), // TODO: add "name" to the login form + } +} diff --git a/packages/collabra/package.json b/packages/collabra/package.json new file mode 100644 index 0000000000..b5430471d2 --- /dev/null +++ b/packages/collabra/package.json @@ -0,0 +1,67 @@ +{ + "name": "xpub-collabra", + "version": "0.0.1", + "description": "xpub configured for Collabra", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://gitlab.coko.foundation/xpub/xpub" + }, + "dependencies": { + "font-awesome": "^4.7.0", + "joi": "^10.4.1", + "loadable-components": "^0.2.1", + "lodash": "^4.17.4", + "moment": "^2.18.1", + "prop-types": "^15.5.10", + "pubsweet": "1.0.0-alpha.4", + "pubsweet-client": "^1.0.0-alpha.1", + "pubsweet-component-ink-backend": "0.0.10", + "pubsweet-component-ink-frontend": "^0.1.0", + "pubsweet-component-login": "^0.3.0", + "pubsweet-component-password-reset-backend": "^0.1.0", + "pubsweet-component-password-reset-frontend": "^0.1.0", + "pubsweet-component-signup": "^0.2.0", + "pubsweet-component-xpub-dashboard": "^0.0.2", + "pubsweet-component-xpub-manuscript": "^0.0.2", + "pubsweet-component-xpub-submit": "^0.0.2", + "pubsweet-server": "^1.0.0-alpha.1", + "pubsweet-theme-plugin": "^0.0.1", + "react": "^15.6.1", + "react-dom": "^15.6.1", + "react-loadable": "^4.0.3", + "react-redux": "^5.0.2", + "react-router": "^3.0.5", + "react-router-redux": "^4.0.7", + "recompose": "^0.25.0", + "redux": "^3.6.0", + "redux-logger": "^3.0.1", + "xpub-fonts": "^0.0.2", + "xpub-selectors": "^0.0.2", + "xpub-ui": "^0.0.2" + }, + "devDependencies": { + "babel-loader": "^7.1.1", + "babel-preset-react-app": "^3.0.2", + "compression-webpack-plugin": "^1.0.0", + "copy-webpack-plugin": "^4.0.1", + "css-loader": "^0.28.4", + "extract-text-webpack-plugin": "^3.0.0", + "file-loader": "^0.11.2", + "html-webpack-plugin": "^2.24.0", + "joi-browser": "^10.0.6", + "node-sass": "^4.5.3", + "react-hot-loader": "^3.0.0-beta.7", + "sass-loader": "^6.0.6", + "string-replace-loader": "^1.3.0", + "style-loader": "^0.18.2", + "webpack": "^3.5.5", + "webpack-dev-middleware": "^1.11.0", + "webpack-hot-middleware": "^2.18.1" + }, + "scripts": { + "setupdb": "pubsweet setupdb --dev ./", + "start": "pubsweet run --dev", + "debug": "node $NODE_DEBUG_OPTION ./node_modules/pubsweet/bin/pubsweet-run.js --dev" + } +} diff --git a/static/pubsweet-rgb-small.jpg b/packages/collabra/static/pubsweet-rgb-small.jpg similarity index 100% rename from static/pubsweet-rgb-small.jpg rename to packages/collabra/static/pubsweet-rgb-small.jpg diff --git a/static/pubsweet.jpg b/packages/collabra/static/pubsweet.jpg similarity index 100% rename from static/pubsweet.jpg rename to packages/collabra/static/pubsweet.jpg diff --git a/packages/collabra/webpack/common-rules.js b/packages/collabra/webpack/common-rules.js new file mode 100644 index 0000000000..d48e5473c4 --- /dev/null +++ b/packages/collabra/webpack/common-rules.js @@ -0,0 +1,105 @@ +const path = require('path') +const components = require('../config/components.json') + +const requireComponentsString = components + .filter(name => { + const component = require(name) + + // "client" or "frontend" for backwards compatibility + return component.client || component.frontend + }) + .map(name => "require('" + name + "')") + .join(', ') + +// paths that use ES6 scripts and CSS modules +// TODO: compile components to ES5 for distribution +const include = [ + path.join(__dirname, '..', 'app'), + /pubsweet-[^/]+\/src/, + /xpub-[^/]+\/src/, + /component-[^/]+\/src/, +] + +module.exports = [ + // replace "PUBSWEET_COMPONENTS" string in pubsweet-client + { + test: /\.js$/, + enforce: 'pre', + // include: /pubsweet-client\/src\/components/, + loader: 'string-replace-loader', + options: { + search: 'PUBSWEET_COMPONENTS', + replace: '[' + requireComponentsString + ']' + } + }, + + // loaders + { + oneOf: [ + // ES6 JS + { + test: /\.js?$/, + include, + loader: 'babel-loader', + options: { + presets: [ + 'env', + 'react-app' + ], + plugins: [ + 'react-hot-loader/babel', + ] + } + }, + + // CSS Modules + { + test: /\.local\.css$/, + include, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: true, + } + } + ] + }, + + // global CSS + { + test: /\.css$/, + use: [ + 'style-loader', + 'css-loader', + ] + }, + + // global SCSS + /*{ + test: /\.scss$/, + use: [ + 'style-loader', + 'css-loader', + 'sass-loader' + ] + },*/ + + // HTML (needed?) + { + test: /\.html$/, + use: 'html-loader' + }, + + // files + { + exclude: [/\.js$/, /\.html$/, /\.json$/], + loader: 'file-loader', + options: { + name: 'static/media/[name].[hash:8].[ext]', + } + } + ] + }, +] diff --git a/webpack/webpack.dev.config.js b/packages/collabra/webpack/webpack.dev.config.js similarity index 81% rename from webpack/webpack.dev.config.js rename to packages/collabra/webpack/webpack.dev.config.js index bac8c4e598..e0e9685e83 100644 --- a/webpack/webpack.dev.config.js +++ b/packages/collabra/webpack/webpack.dev.config.js @@ -1,3 +1,6 @@ +process.env.NODE_ENV = "development" +process.env.BABEL_ENV = "development" + const path = require('path') const webpack = require('webpack') const ThemePlugin = require('pubsweet-theme-plugin') @@ -23,13 +26,13 @@ module.exports = [ filename: '[name].js', publicPath: '/assets/' }, - devtool: 'cheap-module-source-map', + devtool: 'eval', // 'cheap-module-source-map', module: { rules }, resolve: { - symlinks: false, - modules: [ + // symlinks: false, + modules: [ // needed for resolving app/routes path.resolve(__dirname, '..'), path.resolve(__dirname, '..', 'node_modules'), 'node_modules' @@ -37,9 +40,10 @@ module.exports = [ alias: { joi: 'joi-browser' }, - plugins: [new ThemePlugin(config['pubsweet-client'].theme)], - extensions: ['.js', '.jsx', '.json', '.scss'], - enforceExtension: false + plugins: [ + new ThemePlugin(config['pubsweet-client'].theme) + ], + extensions: ['.js', '.jsx'], }, plugins: [ new webpack.HotModuleReplacementPlugin(), diff --git a/webpack/webpack.production.config.js b/packages/collabra/webpack/webpack.production.config.js similarity index 87% rename from webpack/webpack.production.config.js rename to packages/collabra/webpack/webpack.production.config.js index f1ab7bb1db..c0fd1a9c80 100644 --- a/webpack/webpack.production.config.js +++ b/packages/collabra/webpack/webpack.production.config.js @@ -1,3 +1,6 @@ +process.env.NODE_ENV = "production" +process.env.BABEL_ENV = "production" + const path = require('path') const webpack = require('webpack') const ExtractTextPlugin = require('extract-text-webpack-plugin') @@ -26,12 +29,12 @@ module.exports = [ rules }, resolve: { - symlinks: false, - modules: [ - path.resolve(__dirname, '..'), - path.join(__dirname, '..', 'node_modules'), - 'node_modules' - ], + // symlinks: false, + // modules: [ + // path.resolve(__dirname, '..'), + // path.join(__dirname, '..', 'node_modules'), + // 'node_modules' + // ], alias: { joi: 'joi-browser' }, diff --git a/webpack/webpack.test.config.js b/packages/collabra/webpack/webpack.test.config.js similarity index 90% rename from webpack/webpack.test.config.js rename to packages/collabra/webpack/webpack.test.config.js index a76deb1b98..3449be76df 100644 --- a/webpack/webpack.test.config.js +++ b/packages/collabra/webpack/webpack.test.config.js @@ -28,12 +28,12 @@ module.exports = [ rules }, resolve: { - symlinks: false, - modules: [ - path.resolve(__dirname, '..'), - path.resolve(__dirname, '..', 'node_modules'), - 'node_modules' - ], + // symlinks: false, + // modules: [ + // path.resolve(__dirname, '..'), + // path.resolve(__dirname, '..', 'node_modules'), + // 'node_modules' + // ], alias: { joi: 'joi-browser' }, diff --git a/packages/component-dashboard/package.json b/packages/component-dashboard/package.json new file mode 100644 index 0000000000..5b502a3d71 --- /dev/null +++ b/packages/component-dashboard/package.json @@ -0,0 +1,26 @@ +{ + "name": "pubsweet-component-xpub-dashboard", + "version": "0.0.2", + "main": "src", + "author": "Collaborative Knowledge Foundation", + "license": "MIT", + "files": [ + "src", + "dist" + ], + "dependencies": { + "classnames": "^2.2.5", + "pubsweet-component-ink-frontend": "^0.1.0", + "react-dropzone": "^3.13.3", + "react-moment": "^0.6.1", + "xpub-selectors": "^0.0.2" + }, + "peerDependencies": { + "prop-types": "^15.5.10", + "pubsweet-client": "^1.0.0-alpha.1", + "react": "^15.6.1", + "react-dom": "^15.6.1", + "react-redux": "^5.0.2", + "react-router": "^3.0.5" + } +} diff --git a/packages/component-dashboard/src/components/Dashboard.js b/packages/component-dashboard/src/components/Dashboard.js new file mode 100644 index 0000000000..cd0660bdd2 --- /dev/null +++ b/packages/component-dashboard/src/components/Dashboard.js @@ -0,0 +1,24 @@ +import React from 'react' +import classes from './Dashboard.local.css' +import UploadManuscript from './UploadManuscript' +import DashboardItem from './DashboardItem' + +const Dashboard = ({ projects, createProject, createVersion, convertToHTML, isConverting }) => ( + <div className={classes.root}> + <div className={classes.upload}> + <UploadManuscript + createProject={createProject} + createVersion={createVersion} + convertToHTML={convertToHTML}/> + </div> + + {projects.map(project => ( + <DashboardItem + className={classes.item} + key={project.id} + project={project}/> + ))} + </div> +) + +export default Dashboard diff --git a/packages/component-dashboard/src/components/Dashboard.local.css b/packages/component-dashboard/src/components/Dashboard.local.css new file mode 100644 index 0000000000..7a25f58f9e --- /dev/null +++ b/packages/component-dashboard/src/components/Dashboard.local.css @@ -0,0 +1,4 @@ +.root { + max-width: 60em; + margin: auto; +} diff --git a/packages/component-dashboard/src/components/DashboardItem.js b/packages/component-dashboard/src/components/DashboardItem.js new file mode 100644 index 0000000000..87295ea761 --- /dev/null +++ b/packages/component-dashboard/src/components/DashboardItem.js @@ -0,0 +1,9 @@ +import React from 'react' + +const DashboardItem = ({ project }) => ( + <div> + <div>{ project.title || 'Untitled' }</div> + </div> +) + +export default DashboardItem diff --git a/packages/component-dashboard/src/components/DashboardPage.js b/packages/component-dashboard/src/components/DashboardPage.js new file mode 100644 index 0000000000..dba8d4d69b --- /dev/null +++ b/packages/component-dashboard/src/components/DashboardPage.js @@ -0,0 +1,22 @@ +import { compose } from 'recompose' +import { connect } from 'react-redux' +import { orderBy } from 'lodash' +import { actions } from 'pubsweet-client' +import { selectCurrentUser } from 'xpub-selectors' +import { ink } from 'pubsweet-component-ink-frontend/actions' +import Dashboard from './Dashboard' + +export default compose( + connect( + state => ({ + projects: orderBy(state.collections, 'created', 'desc'), + currentUser: selectCurrentUser(state), + isConverting: state.ink.isFetching + }), + { + createProject: actions.createCollection, + createVersion: actions.createFragment, + convertToHTML: ink + } + ) +)(Dashboard) diff --git a/packages/component-dashboard/src/components/UploadManuscript.js b/packages/component-dashboard/src/components/UploadManuscript.js new file mode 100644 index 0000000000..c06fee84d3 --- /dev/null +++ b/packages/component-dashboard/src/components/UploadManuscript.js @@ -0,0 +1,94 @@ +import React from 'react' +import Dropzone from 'react-dropzone' +import classnames from 'classnames' +import classes from './UploadManuscript.local.css' + +// TODO: move isConverting from global state to local state + +const generateTitle = (name) => { + return name + .replace(/[_-]+/g, ' ') // convert hyphens/underscores to space + .replace(/\.[^.]+$/, '') // remove file extension +} + +// TODO: preserve italics (use parse5?) +const extractTitle = (source) => { + const doc = new DOMParser().parseFromString(source, 'text/html') + const heading = doc.querySelector('h1') + + return heading ? heading.textContent : null +} + +class UploadManuscript extends React.Component { + state = { + converting: false, + complete: undefined, + error: undefined + } + + onDrop = acceptedFiles => { + const { convertToHTML, createProject, createVersion } = this.props + + const inputFile = acceptedFiles[0] + + this.setState({ + converting: true, + complete: false, + error: undefined + }) + + convertToHTML(inputFile).then(response => { + if (!response.converted) { + throw new Error('The file was not converted') + } + + const source = response.converted + const title = extractTitle(source) || generateTitle(inputFile.name) + + return createProject({ + type: 'project', + }).then(({ collection: project }) => { + if (!project.id) { + throw new Error('Failed to create a project') + } + + return createVersion(project, { + type: 'version', + version: 1, + source, + title + }).then(() => { + this.setState({ complete: true }) + }) + }) + }).catch(error => { + this.setState({ error: error.message }) + }) + } + + render () { + const { converting, complete, error } = this.props + + return ( + <Dropzone + onDrop={this.onDrop} + accept="application/vnd.openxmlformats-officedocument.wordprocessingml.document" + className={classes.dropzone}> + <div className={classes.main}> + <div className={classnames(classes.icon, { + [classes.converting]: converting, + [classes.complete]: complete + })}> + <span>+</span> + </div> + + <div className={classes.info}> + {error ? error : 'Start a new submission'} + </div> + </div> + </Dropzone> + ) + } +} + +export default UploadManuscript diff --git a/packages/component-dashboard/src/components/UploadManuscript.local.css b/packages/component-dashboard/src/components/UploadManuscript.local.css new file mode 100644 index 0000000000..a916460723 --- /dev/null +++ b/packages/component-dashboard/src/components/UploadManuscript.local.css @@ -0,0 +1,26 @@ +.dropzone { + border: none; + display: inline-block; + cursor: pointer; +} + +.main { + font-weight: 200; + display: flex; + padding-top: 10px; + padding-bottom: 10px; +} + +.icon { + color: cornflowerblue; +} + +.converting { + color: orange; +} + +.info { + text-transform: uppercase; + font-size: 200%; + color: cornflowerblue; +} diff --git a/packages/component-dashboard/src/components/index.js b/packages/component-dashboard/src/components/index.js new file mode 100644 index 0000000000..28b6c656bd --- /dev/null +++ b/packages/component-dashboard/src/components/index.js @@ -0,0 +1 @@ +module.exports = require('./DashboardPage') diff --git a/packages/component-dashboard/src/index.js b/packages/component-dashboard/src/index.js new file mode 100644 index 0000000000..243220c285 --- /dev/null +++ b/packages/component-dashboard/src/index.js @@ -0,0 +1,7 @@ +module.exports = { + frontend: { + components: [ + () => require('./components') + ] + }, +} diff --git a/packages/component-manuscript/package.json b/packages/component-manuscript/package.json new file mode 100644 index 0000000000..cfd77ba020 --- /dev/null +++ b/packages/component-manuscript/package.json @@ -0,0 +1,43 @@ +{ + "name": "pubsweet-component-xpub-manuscript", + "version": "0.0.2", + "files": [ + "src", + "dist" + ], + "main": "src", + "dependencies": { + "pubsweet-component-wax": "^0.1.0", + "xpub-selectors": "^0.0.2" + }, + "peerDependencies": { + "prop-types": "^15.5.10", + "pubsweet-client": "^1.0.0-alpha.1", + "react": "^15.6.1", + "react-dom": "^15.6.1", + "react-redux": "^5.0.2", + "react-router": "^3.0.5" + }, + "devDependencies": { + "babel-loader": "^7.1.1", + "babel-preset-env": "^1.6.0", + "babel-preset-minify": "^0.2.0", + "babel-preset-react-app": "^3.0.2", + "css-loader": "^0.28.4", + "eslint": "^4.4.1", + "extract-text-webpack-plugin": "^3.0.0", + "file-loader": "^0.11.2", + "node-sass": "^4.5.3", + "rimraf": "^2.6.1", + "sass-loader": "^6.0.6", + "style-loader": "^0.18.2", + "webpack": "^3.5.5", + "webpack-node-externals": "^1.6.0" + }, + "scripts": { + "prebuild": "npm run clean && npm run lint", + "lint": "eslint src", + "clean": "rimraf dist", + "build": "NODE_ENV=production webpack --progress --profile" + } +} diff --git a/packages/component-manuscript/src/components/Manuscript.js b/packages/component-manuscript/src/components/Manuscript.js new file mode 100644 index 0000000000..3480db693b --- /dev/null +++ b/packages/component-manuscript/src/components/Manuscript.js @@ -0,0 +1,26 @@ +import React from 'react' +import { browserHistory } from 'react-router' +import SimpleEditor from 'pubsweet-component-wax/src/SimpleEditor' +import 'pubsweet-component-wax/src/SimpleEditor.scss' +import classes from './Manuscript.local.css' + +// TODO: convert user teams to roles (see SimpleEditorWrapper)? + +const Manuscript = ({ project, version, currentUser, fileUpload, updateVersion }) => ( + <SimpleEditor + classes={classes.fullscreen} + book={project} + fragment={version} + user={currentUser} + fileUpload={fileUpload} + history={browserHistory} + update={data => ( + updateVersion(project, { id: version.id, ...data }) + )} + onSave={source => ( + updateVersion(project, { id: version.id, source }) + )} + /> +) + +export default Manuscript diff --git a/packages/component-manuscript/src/components/Manuscript.local.css b/packages/component-manuscript/src/components/Manuscript.local.css new file mode 100644 index 0000000000..6b2a948fa3 --- /dev/null +++ b/packages/component-manuscript/src/components/Manuscript.local.css @@ -0,0 +1,9 @@ +.fullscreen { + position: fixed; + top: 50px; + left: 0; + right: 0; + bottom: 0; + background: white; + overflow: hidden; +} diff --git a/packages/component-manuscript/src/components/ManuscriptPage.js b/packages/component-manuscript/src/components/ManuscriptPage.js new file mode 100644 index 0000000000..8f8a3441a3 --- /dev/null +++ b/packages/component-manuscript/src/components/ManuscriptPage.js @@ -0,0 +1,19 @@ +import { compose } from 'recompose' +import { connect } from 'react-redux' +import { actions } from 'pubsweet-client' +import { selectCurrentUser, selectCollection, selectFragment } from 'xpub-selectors' +import Manuscript from './Manuscript' + +export default compose( + connect( + (state, ownProps) => ({ + currentUser: selectCurrentUser(state), + project: selectCollection(state, ownProps.params.project), + version: selectFragment(state, ownProps.params.version) + }), + { + fileUpload: actions.fileUpload, + updateVersion: actions.updateFragment + } + ) +)(Manuscript) diff --git a/packages/component-manuscript/src/components/index.js b/packages/component-manuscript/src/components/index.js new file mode 100644 index 0000000000..4d5baac5a2 --- /dev/null +++ b/packages/component-manuscript/src/components/index.js @@ -0,0 +1 @@ +module.exports = require('./ManuscriptPage') diff --git a/packages/component-manuscript/src/index.js b/packages/component-manuscript/src/index.js new file mode 100644 index 0000000000..0225ff41f4 --- /dev/null +++ b/packages/component-manuscript/src/index.js @@ -0,0 +1,7 @@ +module.exports = { + frontend: { + components: [ + () => require('./components') + ] + } +} diff --git a/packages/component-manuscript/webpack.config.js b/packages/component-manuscript/webpack.config.js new file mode 100644 index 0000000000..2fb4f5258d --- /dev/null +++ b/packages/component-manuscript/webpack.config.js @@ -0,0 +1,86 @@ +process.env.BABEL_ENV = 'production' +process.env.NODE_ENV = 'production' + +const path = require('path') +const nodeExternals = require('webpack-node-externals') +const ExtractTextPlugin = require('extract-text-webpack-plugin') + +module.exports = { + entry: './src/index.js', + output: { + filename: 'index.js', + path: path.join(__dirname, 'dist'), + library: 'WaxEditor', + libraryTarget: 'commonjs2' + }, + devtool: 'cheap-module-source-map', + externals: [nodeExternals({ + whitelist: [/\.(?!js$).{1,5}$/i] + })], + resolve: { + extensions: ['.js', '.jsx'], // needed because pubsweet-component-wax uses jsx + }, + module: { + rules: [ + { + oneOf: [ + { + test: /\.jsx?$/, + include: [ + path.join(__dirname, 'src'), + /pubsweet-[^/]+\/src/, + ], + loader: 'babel-loader', + options: { + presets: ['minify', 'react-app'] + } + }, + + // CSS modules + { + test: /\.local\.css$/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: true + } + } + ] + }, + + // CSS + { + test: /\.css$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: 'css-loader' + }) + }, + + // SCSS + { + test: /\.scss$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: ['css-loader', 'sass-loader'], + }), + }, + + // other files + { + exclude: [/\.jsx?$/, /\.html$/, /\.json$/], + loader: 'file-loader', + options: { + name: 'static/[name].[hash:8].[ext]', + } + } + ] + } + ] + }, + plugins: [ + new ExtractTextPlugin('styles.css'), + ] +} diff --git a/packages/component-submit/package.json b/packages/component-submit/package.json new file mode 100644 index 0000000000..9f2e5ec491 --- /dev/null +++ b/packages/component-submit/package.json @@ -0,0 +1,25 @@ +{ + "name": "pubsweet-component-xpub-submit", + "version": "0.0.2", + "main": "src", + "author": "Collaborative Knowledge Foundation", + "license": "MIT", + "files": [ + "src", + "dist" + ], + "dependencies": { + "formsy-react": "^0.19.2", + "formsy-react-components": "^0.10.1", + "uuid": "^3.1.0", + "xpub-selectors": "^0.0.2" + }, + "peerDependencies": { + "prop-types": "^15.5.10", + "pubsweet-client": "^1.0.0-alpha.1", + "react": "^15.6.1", + "react-dom": "^15.6.1", + "react-redux": "^5.0.2", + "react-router": "^3.0.5" + } +} diff --git a/packages/component-submit/src/components/Submit.js b/packages/component-submit/src/components/Submit.js new file mode 100644 index 0000000000..ef389e9e53 --- /dev/null +++ b/packages/component-submit/src/components/Submit.js @@ -0,0 +1,9 @@ +import React from 'react' +import classes from './Submit.local.css' + +const Submit = ({ project, version, updateVersion }) => ( + <div className={classes.root}> + </div> +) + +export default Submit diff --git a/packages/component-submit/src/components/Submit.local.css b/packages/component-submit/src/components/Submit.local.css new file mode 100644 index 0000000000..bedeb67b49 --- /dev/null +++ b/packages/component-submit/src/components/Submit.local.css @@ -0,0 +1,3 @@ +.root { + +} diff --git a/packages/component-submit/src/components/SubmitPage.js b/packages/component-submit/src/components/SubmitPage.js new file mode 100644 index 0000000000..555531b738 --- /dev/null +++ b/packages/component-submit/src/components/SubmitPage.js @@ -0,0 +1,17 @@ +import { compose } from 'recompose' +import { connect } from 'react-redux' +import { actions } from 'pubsweet-client' +import { selectCollection, selectFragment } from 'xpub-selectors' +import Submit from './Submit' + +export default compose( + connect( + (state, ownProps) => ({ + project: selectCollection(state, ownProps.params.project), + version: selectFragment(state, ownProps.params.version) + }), + { + updateVersion: actions.updateFragment + } + ) +)(Submit) diff --git a/packages/component-submit/src/components/index.js b/packages/component-submit/src/components/index.js new file mode 100644 index 0000000000..38dfee8241 --- /dev/null +++ b/packages/component-submit/src/components/index.js @@ -0,0 +1 @@ +module.exports = require('./SubmitPage') diff --git a/packages/component-submit/src/index.js b/packages/component-submit/src/index.js new file mode 100644 index 0000000000..0225ff41f4 --- /dev/null +++ b/packages/component-submit/src/index.js @@ -0,0 +1,7 @@ +module.exports = { + frontend: { + components: [ + () => require('./components') + ] + } +} diff --git a/packages/component-submit/src/lib/date.js b/packages/component-submit/src/lib/date.js new file mode 100644 index 0000000000..fa2d161cc8 --- /dev/null +++ b/packages/component-submit/src/lib/date.js @@ -0,0 +1,3 @@ +import moment from 'moment' + +export const format = date => moment(date).format('YYYY-MM-DD') diff --git a/packages/component-submit/src/lib/sort.js b/packages/component-submit/src/lib/sort.js new file mode 100644 index 0000000000..1ffa02a35b --- /dev/null +++ b/packages/component-submit/src/lib/sort.js @@ -0,0 +1,3 @@ +export const ascending = field => (a, b) => a[field] - b[field] + +export const descending = field => (a, b) => b[field] - a[field] diff --git a/packages/component-submit/src/lib/text.js b/packages/component-submit/src/lib/text.js new file mode 100644 index 0000000000..e2e4b4ac34 --- /dev/null +++ b/packages/component-submit/src/lib/text.js @@ -0,0 +1 @@ +export const ucfirst = (text) => text.substr(0, 1).toUpperCase() + text.substr(1) diff --git a/packages/xpub-fonts/package.json b/packages/xpub-fonts/package.json new file mode 100644 index 0000000000..a948108d27 --- /dev/null +++ b/packages/xpub-fonts/package.json @@ -0,0 +1,12 @@ +{ + "name": "xpub-fonts", + "version": "0.0.2", + "description": "Fonts for use in xpub clients", + "main": "src", + "license": "MIT", + "dependencies": { + "pubsweet-fira": "^0.0.3", + "typeface-fira-sans-condensed": "^0.0.35", + "typeface-vollkorn": "^0.0.35" + } +} diff --git a/packages/xpub-fonts/src/index.js b/packages/xpub-fonts/src/index.js new file mode 100644 index 0000000000..d8d4231df8 --- /dev/null +++ b/packages/xpub-fonts/src/index.js @@ -0,0 +1,3 @@ +require('typeface-fira-sans-condensed') +require('typeface-vollkorn') +require('pubsweet-fira') diff --git a/packages/xpub-selectors/package.json b/packages/xpub-selectors/package.json new file mode 100644 index 0000000000..97a7f5d0bd --- /dev/null +++ b/packages/xpub-selectors/package.json @@ -0,0 +1,7 @@ +{ + "name": "xpub-selectors", + "version": "0.0.2", + "description": "Redux selectors for use in xpub components", + "main": "src", + "license": "MIT" +} diff --git a/packages/xpub-selectors/src/index.js b/packages/xpub-selectors/src/index.js new file mode 100644 index 0000000000..561a5e3b5c --- /dev/null +++ b/packages/xpub-selectors/src/index.js @@ -0,0 +1,14 @@ +export const selectCurrentUser = state => state.currentUser.isAuthenticated + ? state.currentUser.user + : null + +// TODO: collections should be keyed by id +export const selectCollection = (state, id) => state.collections + .find(collection => collection.id === id) + +// TODO: there shouldn't be any missing +export const selectFragments = (state, ids) => ids + .map(id => state.fragments[id]) + .filter(fragment => fragment) + +export const selectFragment = (state, id) => state.fragments[id] diff --git a/packages/xpub-ui/.eslintrc b/packages/xpub-ui/.eslintrc new file mode 100644 index 0000000000..db7f6d5a86 --- /dev/null +++ b/packages/xpub-ui/.eslintrc @@ -0,0 +1,7 @@ +{ + "globals": { + "initialState": true, + "state": false, + "setState": false + } +} diff --git a/packages/xpub-ui/lib/styleguide/StyleGuideRenderer.js b/packages/xpub-ui/lib/styleguide/StyleGuideRenderer.js new file mode 100644 index 0000000000..3a7d1428ca --- /dev/null +++ b/packages/xpub-ui/lib/styleguide/StyleGuideRenderer.js @@ -0,0 +1,25 @@ +import React from 'react' +import 'xpub-fonts' +import classes from './StyleGuideRenderer.local.css' + +const StyleGuideRenderer = ({ title, children, toc }) => { + return ( + <div className={classes.root}> + <div className={classes.sidebar}> + <header className={classes.header}> + <h1 className={classes.title}>{title}</h1> + </header> + + <nav className={classes.nav}> + {toc} + </nav> + </div> + + <div className={classes.content}> + {children} + </div> + </div> + ) +} + +export default StyleGuideRenderer diff --git a/packages/xpub-ui/lib/styleguide/StyleGuideRenderer.local.css b/packages/xpub-ui/lib/styleguide/StyleGuideRenderer.local.css new file mode 100644 index 0000000000..ddcdbb3307 --- /dev/null +++ b/packages/xpub-ui/lib/styleguide/StyleGuideRenderer.local.css @@ -0,0 +1,37 @@ +.root { + display: grid; + grid-template-columns: 1fr 3fr; + grid-template-areas: "side content"; + height: 100vh; + width: 100vw; +} + +.sidebar { + grid-area: side; + display: flex; + flex-direction: column; + overflow-y: hidden; +} + +.content { + grid-area: content; + padding: 1rem; + overflow-y: auto; +} + +.header { + padding: 0.5rem; +} + +.nav { + flex: 1; + overflow-y: auto; + padding: 0.5rem; +} + +.title { + font-family: "Fira Sans", serif; + font-size: 1rem; + margin-bottom: 0; + padding: 0 1rem; +} diff --git a/packages/xpub-ui/lib/styleguide/Wrapper.js b/packages/xpub-ui/lib/styleguide/Wrapper.js new file mode 100644 index 0000000000..c6be0fff02 --- /dev/null +++ b/packages/xpub-ui/lib/styleguide/Wrapper.js @@ -0,0 +1,21 @@ +import React from 'react' +// import { Router, createMemoryHistory } from 'react-router' +import 'xpub-fonts' +import './Wrapper.scss' +import classes from './Wrapper.local.css' + +/*const Wrapper = ({ children }) => ( + <Router history={createMemoryHistory()}> + <div className={classes.root}> + {children} + </div> + </Router> +)*/ + +const Wrapper = ({ children }) => ( + <div className={classes.root}> + {children} + </div> +) + +export default Wrapper diff --git a/packages/xpub-ui/lib/styleguide/Wrapper.local.css b/packages/xpub-ui/lib/styleguide/Wrapper.local.css new file mode 100644 index 0000000000..a72adc6609 --- /dev/null +++ b/packages/xpub-ui/lib/styleguide/Wrapper.local.css @@ -0,0 +1,3 @@ +.root { + font-family: "Fira Sans", sans-serif; +} diff --git a/packages/xpub-ui/lib/styleguide/Wrapper.scss b/packages/xpub-ui/lib/styleguide/Wrapper.scss new file mode 100644 index 0000000000..b5321f7d70 --- /dev/null +++ b/packages/xpub-ui/lib/styleguide/Wrapper.scss @@ -0,0 +1,4 @@ +// TODO: variables + +@import '~bootstrap-sass/assets/stylesheets/_bootstrap'; + diff --git a/packages/xpub-ui/package.json b/packages/xpub-ui/package.json new file mode 100644 index 0000000000..0bffb1552b --- /dev/null +++ b/packages/xpub-ui/package.json @@ -0,0 +1,34 @@ +{ + "name": "xpub-ui", + "version": "0.0.2", + "files": [ + "docs", + "dist", + "src" + ], + "main": "src", + "jsnext:main": "src", + "dependencies": { + "xpub-fonts": "^0.0.2", + "classnames": "^2.2.5", + "lodash": "^4.17.4", + "prop-types": "^15.5.10", + "react": "^15.6.1", + "react-dom": "^15.6.1", + "react-router": "^3.0.5" + }, + "devDependencies": { + "babel-preset-react-app": "^3.0.2", + "faker": "^4.1.0", + "react-styleguidist": "^6.0.8", + "webpack": "^3.5.5" + }, + "scripts": { + "styleguide": "styleguidist server", + "styleguide:build": "styleguidist build", + "clean": "rimraf dist", + "lint": "eslint src", + "prebuild": "npm run clean && npm run lint", + "build": "webpack --progress --profile" + } +} diff --git a/packages/xpub-ui/src/AppBar.js b/packages/xpub-ui/src/AppBar.js new file mode 100644 index 0000000000..1aa41a8911 --- /dev/null +++ b/packages/xpub-ui/src/AppBar.js @@ -0,0 +1,27 @@ +import React from 'react' +import { Link } from 'react-router' +import classnames from 'classnames' +import classes from './AppBar.local.css' + +const AppBar = ({ brandLink, brandName, loginLink, logoutLink, userName }) => ( + <div className={classes.root}> + <Link to={brandLink || '/'} + className={classes.link}>{brandName}</Link> + + <div> + {userName && ( + <span className={classes.item}>{userName}</span> + )} + + {userName ? ( + <Link to={logoutLink} + className={classnames(classes.item, classes.link)}>logout</Link> + ) : ( + <Link to={loginLink} + className={classnames(classes.item, classes.link)}>login</Link> + )} + </div> + </div> +) + +export default AppBar diff --git a/packages/xpub-ui/src/AppBar.local.css b/packages/xpub-ui/src/AppBar.local.css new file mode 100644 index 0000000000..40bc1c5482 --- /dev/null +++ b/packages/xpub-ui/src/AppBar.local.css @@ -0,0 +1,16 @@ +.root { + display: flex; + justify-content: space-between; +} + +.link { + color: cornflowerblue; +} + +.link:hover { + text-decoration: underline; +} + +.item { + padding: 1rem; +} diff --git a/packages/xpub-ui/src/AppBar.md b/packages/xpub-ui/src/AppBar.md new file mode 100644 index 0000000000..a2b7acebcb --- /dev/null +++ b/packages/xpub-ui/src/AppBar.md @@ -0,0 +1,20 @@ +The app bar appears at the top of every page of the application. + +It displays the name of the application (as a link to the home page), the username of the current user, and a link to sign out. + +```js +<AppBar + brandName="xpub" + loginLink="/login" + logoutLink="/logout" + userName="foo"/> +``` + +When the user is not signed in, only the login link is displayed. + +```js +<AppBar + brandName="xpub" + loginLink="/login" + logoutLink="/logout"/> +``` diff --git a/packages/xpub-ui/src/Radio.js b/packages/xpub-ui/src/Radio.js new file mode 100644 index 0000000000..ac5b499fad --- /dev/null +++ b/packages/xpub-ui/src/Radio.js @@ -0,0 +1,18 @@ +import React from 'react' +import classes from './Radio.local.css' + +const Radio = ({ name, value, label, checked, required, handleChange }) => ( + <label className={classes.root}> + <input + className={classes.input} + type="radio" + name={name} + value={value} + checked={checked} + required={required} + onChange={event => handleChange(event.target.value)}/> + {label} + </label> +) + +export default Radio diff --git a/packages/xpub-ui/src/Radio.local.css b/packages/xpub-ui/src/Radio.local.css new file mode 100644 index 0000000000..5a5e4d035c --- /dev/null +++ b/packages/xpub-ui/src/Radio.local.css @@ -0,0 +1,13 @@ +.root { + display: inline-flex; + align-items: center; + cursor: pointer; +} + +.root:not(:last-child) { + margin-right: 1rem; +} + +.input { + margin-right: 0.25rem; +} diff --git a/packages/xpub-ui/src/Radio.md b/packages/xpub-ui/src/Radio.md new file mode 100644 index 0000000000..ef4d99f262 --- /dev/null +++ b/packages/xpub-ui/src/Radio.md @@ -0,0 +1,25 @@ +A radio button. + +```js +<Radio + name="foo" + handleChange={event => console.log(event)}/> +``` + +A checked radio button. + +```js +<Radio + name="bar" + checked + handleChange={event => console.log(event)}/> +``` + +A radio button with a label. + +```js +<Radio + name="foo" + label="Foo" + handleChange={event => console.log(event)}/> +``` diff --git a/packages/xpub-ui/src/RadioGroup.js b/packages/xpub-ui/src/RadioGroup.js new file mode 100644 index 0000000000..fd9fb27ecb --- /dev/null +++ b/packages/xpub-ui/src/RadioGroup.js @@ -0,0 +1,19 @@ +import React from 'react' +import Radio from './Radio' + +const RadioGroup = ({ name, value, options, required, handleChange }) => ( + <div> + {options.map(option => ( + <Radio + key={option.value} + name={name} + required={required} + value={option.value} + label={option.label} + checked={option.value === value} + handleChange={handleChange}/> + ))} + </div> +) + +export default RadioGroup diff --git a/packages/xpub-ui/src/RadioGroup.md b/packages/xpub-ui/src/RadioGroup.md new file mode 100644 index 0000000000..91611a87c5 --- /dev/null +++ b/packages/xpub-ui/src/RadioGroup.md @@ -0,0 +1,26 @@ +A group of radio buttons. + +```js +initialState = { foo: undefined }; + +const options = [ + { + value: 'one', + label: 'One' + }, + { + value: 'two', + label: 'Two' + }, + { + value: 'three', + label: 'Three' + } +]; + +<RadioGroup + options={options} + name="foo" + value={state.foo} + handleChange={foo => setState({ foo })}/> +``` diff --git a/packages/xpub-ui/src/YesOrNo.js b/packages/xpub-ui/src/YesOrNo.js new file mode 100644 index 0000000000..d8e36bce2d --- /dev/null +++ b/packages/xpub-ui/src/YesOrNo.js @@ -0,0 +1,25 @@ +import React from 'react' +import RadioGroup from './RadioGroup' +import classes from './YesOrNo.local.css' + +const options = [ + { + value: 'yes', + label: 'Yes' + }, + { + value: 'no', + label: 'No' + } +] + +const YesOrNo = (name, value, handleChange) => ( + <RadioGroup + className={classes.root} + name={name} + options={options} + value={value} + handleChange={handleChange}/> +) + +export default YesOrNo diff --git a/packages/xpub-ui/src/YesOrNo.local.css b/packages/xpub-ui/src/YesOrNo.local.css new file mode 100644 index 0000000000..402cdb661c --- /dev/null +++ b/packages/xpub-ui/src/YesOrNo.local.css @@ -0,0 +1,3 @@ +:root { + color: darkgreen; +} diff --git a/packages/xpub-ui/src/YesOrNo.md b/packages/xpub-ui/src/YesOrNo.md new file mode 100644 index 0000000000..4f6cb070e9 --- /dev/null +++ b/packages/xpub-ui/src/YesOrNo.md @@ -0,0 +1,10 @@ +A group of radio buttons that provides just two options: "Yes" or "No" + +```js +initialState = { foo: undefined }; + +<YesOrNo + name="foo" + value={state.foo} + onChange={event => setState({ foo: event.target.value })}/> +``` diff --git a/packages/xpub-ui/src/index.js b/packages/xpub-ui/src/index.js new file mode 100644 index 0000000000..051f5df2b0 --- /dev/null +++ b/packages/xpub-ui/src/index.js @@ -0,0 +1,2 @@ +export { default as AppBar } from './AppBar' +export { default as YesOrNo } from './YesOrNo' diff --git a/packages/xpub-ui/styleguide.config.js b/packages/xpub-ui/styleguide.config.js new file mode 100644 index 0000000000..1361625af6 --- /dev/null +++ b/packages/xpub-ui/styleguide.config.js @@ -0,0 +1,23 @@ +const path = require('path') + +module.exports = { + title: 'xpub-ui style guide', + styleguideComponents: { + StyleGuideRenderer: path.join(__dirname, 'lib/styleguide/StyleGuideRenderer'), + Wrapper: path.join(__dirname, 'lib/styleguide/Wrapper'), + }, + context: { + faker: 'faker', + }, + components: './src/*.js', + skipComponentsWithoutExample: true, + webpackConfig: require('./webpack.config.js'), + theme: { + fontFamily: { + base: '"Fira Sans", sans-serif' + }, + color: { + link: 'cornflowerblue' + } + }, +} diff --git a/packages/xpub-ui/webpack.config.js b/packages/xpub-ui/webpack.config.js new file mode 100644 index 0000000000..eb924224b6 --- /dev/null +++ b/packages/xpub-ui/webpack.config.js @@ -0,0 +1,78 @@ +process.env.BABEL_ENV = 'development' +process.env.NODE_ENV = 'development' + +const path = require('path') +const nodeExternals = require('webpack-node-externals') + +module.exports = { + entry: './src/index.js', + output: { + filename: 'index.js', + path: path.join(__dirname, 'dist'), + library: 'xpub-ui', + libraryTarget: 'commonjs2' + }, + devtool: 'cheap-module-source-map', + externals: [nodeExternals({ + whitelist: [/\.(?!js$).{1,5}$/i] + })], + module: { + rules: [ + { + oneOf: [ + // ES6 modules + { + test: /\.js$/, + include: [ + path.join(__dirname, 'src'), + path.join(__dirname, 'lib'), + ], + loader: 'babel-loader', + options: { + presets: [ + 'react-app', + ], + cacheDirectory: true, + }, + }, + + // CSS modules + { + test: /\.local\.css$/, + include: [ + path.join(__dirname, 'src'), + path.join(__dirname, 'lib'), + ], + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: true, + } + } + ], + }, + + // CSS + { + test: /\.css$/, + use: [ + 'style-loader', + 'css-loader' + ], + }, + + // Files + { + exclude: [/\.js$/, /\.html$/, /\.json$/], + loader: 'file-loader', + options: { + name: 'static/media/[name].[hash:8].[ext]', + } + } + ] + } + ] + } +} diff --git a/permissions/index.js b/permissions/index.js deleted file mode 100644 index 1f51be4d31..0000000000 --- a/permissions/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = (user, operation, project, version) => { - return true // TODO -} diff --git a/webpack/common-rules.js b/webpack/common-rules.js deleted file mode 100644 index d0ef41b351..0000000000 --- a/webpack/common-rules.js +++ /dev/null @@ -1,114 +0,0 @@ -const path = require('path') -const components = require('./components') - -const modulesPath = path.join(__dirname, '..', 'node_modules') - -module.exports = [ - { - test: /\.(js|jsx)$/, - exclude: /node_modules\/(?!pubsweet-)/, - use: { - loader: 'babel-loader', - options: { - presets: [ - require.resolve('babel-preset-stage-2'), - require.resolve('babel-preset-react'), - [require.resolve('babel-preset-env'), { - modules: false - }] - ], - plugins: [ - 'react-hot-loader/babel', - require.resolve('babel-plugin-transform-class-properties') - ], - env: { - production: { - presets: ['babili'] - } - } - } - } - }, - { - test: /\.png$/, - use: { - loader: 'url-loader' - } - }, - { - test: /\.(woff|woff2|svg|eot|ttf)$/, - use: { - loader: 'url-loader', - options: { - prefix: 'font', - limit: 1000 - } - } - }, - { - test: /\.html$/, - use: { - loader: 'html-loader' - } - }, - { - test: /\.json$/, - use: { - loader: 'json-loader' - } - }, - { - test: /\.(css|scss)$/, - exclude: /\.local\.s?css$/, // Exclude local styles from global - use: [ - { - loader: 'style-loader' - }, - { - loader: 'css-loader' - }, - { - loader: 'sass-loader', - options: { - includePaths: [modulesPath] - } - } - ] - }, - { - test: /\.(css|scss)$/, - include: /\.local\.s?css$/, // Local styles - use: [ - { - loader: 'style-loader' - }, - { - loader: 'css-loader', - options: { - modules: true, - importLoaders: 1 - } - }, - { - loader: 'sass-loader', - options: { - includePaths: [modulesPath] - } - } - ] - }, - { - test: /\.(js)$/, - enforce: 'pre', - exclude: /node_modules\/(?!pubsweet-)/, - use: { - loader: 'string-replace-loader', - options: { - search: 'PUBSWEET_COMPONENTS', - replace: '[' + components.frontend - .map(component => `require('${component}')`) - .join(', ') + ']' // TODO: move the brackets into pubsweet-client - } - } - } -] diff --git a/webpack/components.js b/webpack/components.js deleted file mode 100644 index fbe479b4f4..0000000000 --- a/webpack/components.js +++ /dev/null @@ -1,8 +0,0 @@ -const config = require('../config/shared.js') - -const components = config.pubsweet.components - -module.exports = { - frontend: components.filter(component => require(component).frontend), - backend: components.filter(component => require(component).backend) -} -- GitLab