Commit 3b238891 authored by Jure's avatar Jure

chore: reorganize components into client and server

parent 4403312b
Pipeline #12099 failed with stages
in 4 minutes and 37 seconds
......@@ -17,7 +17,7 @@ module.exports = api => {
presets: ['@babel/preset-env'],
},
{
test: [/\/components\/(?!model-)/],
test: [/\/components\/client/],
presets: ['@babel/preset-env', '@babel/preset-react'],
plugins: [
'@babel/plugin-proposal-class-properties',
......@@ -25,7 +25,7 @@ module.exports = api => {
],
},
{
test: ['./components/model-*'],
test: ['./components/server/*'],
presets: [['@babel/preset-env', { targets: { node: true } }]],
},
]
......
This diff is collapsed.
This diff is collapsed.
......@@ -12,15 +12,7 @@ Package layout
All modules forming part of the public API of the component should be exported from the index.js. Deep imports are discouraged (e.g. `package-name/some/file`).
The component's `index.js` should export e.g.:
```js
module.exports = {
client: {
components: [() => require('./LoginContainer')],
},
}
```
The component's `index.js` should export the React component/s.
### Server components
......
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
import React from 'react'
import { Link, withRouter } from 'react-router-dom'
import PropTypes from 'prop-types'
import queryString from 'query-string'
import styled from 'styled-components'
import { TextField, Button } from '@pubsweet/ui'
import { th } from '@pubsweet/ui-toolkit'
import * as api from 'pubsweet-client/src/helpers/api'
const Root = styled.div`
margin: 0 auto;
width: 40ch;
`
const Alert = styled.div`
color: ${th('colorError')};
`
class PasswordReset extends React.Component {
constructor(props) {
super(props)
this.state = this.getInitialState()
}
getInitialState() {
return {
username: '',
emailSent: false,
emailError: false,
emailErrorMessage: null,
emailSending: false,
password: '',
passwordChanged: false,
passwordError: false,
passwordErrorMessage: null,
passwordSending: false,
}
}
componentWillReceiveProps() {
this.setState(this.getInitialState())
}
static post(data) {
return api.create('/password-reset', data)
}
parsedQuery() {
return queryString.parse(this.props.location.search)
}
handleUsernameChange = e => {
this.setState({ username: e.target.value })
}
handlePasswordChange = e => {
this.setState({ password: e.target.value })
}
handleUsernameSubmit = e => {
e.preventDefault()
const { username } = this.state
if (username) {
this.initiatePasswordReset({ username })
} else {
this.setState({ emailError: 'Please enter a username' })
}
}
handlePasswordSubmit = e => {
e.preventDefault()
const { token, username } = this.parsedQuery()
const { password } = this.state
// TODO: enter twice and confirm?
if (password) {
this.resetPassword({ password, token, username })
} else {
this.setState({ passwordError: 'Please enter a new password' })
}
}
async initiatePasswordReset(data) {
this.setState({
emailSent: false,
emailError: false,
emailErrorMessage: null,
emailSending: true,
})
try {
await PasswordReset.post(data)
this.setState({
emailSent: true,
emailError: false,
emailSending: false,
})
} catch (err) {
this.setState({
emailError: true,
emailErrorMessage: err.error || 'There was an unexpected error',
emailSending: false,
})
}
}
async resetPassword(data) {
this.setState({
passwordChanged: false,
passwordError: false,
passwordErrorMessage: null,
passwordSending: true,
})
try {
await PasswordReset.post(data)
this.setState({
passwordChanged: true,
passwordError: false,
passwordSending: false,
})
} catch (err) {
this.setState({
passwordError: true,
passwordErrorMessage: err.error || 'There was an unexpected error',
passwordSending: false,
})
}
}
render() {
const {
username,
emailSent,
emailErrorMessage,
emailSending,
password,
passwordChanged,
passwordErrorMessage,
passwordSending,
} = this.state
const { token } = this.parsedQuery()
const buildForm = () => {
if (passwordChanged) {
return (
<Alert>
Your password has been changed, you can now{' '}
<Link to="/login">login with the new password</Link>.
</Alert>
)
}
if (token) {
// TODO: validate token on page load?
return (
<form onSubmit={this.handlePasswordSubmit}>
<TextField
label="New password"
onChange={this.handlePasswordChange}
placeholder="Enter a new password…"
type="password"
value={password}
/>
<Button disabled={passwordSending} type="submit">
{passwordSending ? 'Saving…' : 'Save new password'}
</Button>
</form>
)
}
if (emailSent) {
return (
<Alert bsStyle="success">
An email has been sent containing further instructions.
</Alert>
)
}
// TODO: allow email instead of username?
return (
<form onSubmit={this.handleUsernameSubmit}>
<TextField
label="Username"
onChange={this.handleUsernameChange}
placeholder="Enter your username"
type="text"
value={username}
/>
<Button disabled={emailSending} type="submit">
{emailSending ? 'Sending…' : 'Send email'}
</Button>
</form>
)
}
const buildError = error => {
if (!error) return null
return (
<Alert bsStyle="warning">
<i className="fa fa-exclamation-circle" /> {buildErrorMessage(error)}
</Alert>
)
}
const buildErrorMessage = error => {
if (error === 'expired') {
return (
<span>
The token is only valid for 24 hours, please{' '}
<Link to="/password-reset">request a new password reset email</Link>
</span>
)
}
if (error === 'invalid') {
return (
<span>
The token is no longer valid, please{' '}
<Link to="/password-reset">request a new password reset email</Link>
</span>
)
}
return error
}
return (
<Root>
{buildError(emailErrorMessage)}
{buildError(passwordErrorMessage)}
<h1>Password reset</h1>
{buildForm()}
<Link to="/login">Return to login form</Link>
</Root>
)
}
}
PasswordReset.propTypes = {
location: PropTypes.object.isRequired,
}
export default withRouter(PasswordReset)
import PasswordReset from './PasswordReset'
export default PasswordReset
{
"name": "pubsweet-component-form-group",
"version": "2.0.9",
"description": "Form component with validation support for PubSweet",
"name": "pubsweet-component-password-reset-frontend",
"version": "3.0.7",
"description": "Password reset frontend component for PubSweet",
"main": "index.js",
"author": "Collaborative Knowledge Foundation",
"license": "MIT",
"dependencies": {
"joi-browser": "^13.4.0",
"prop-types": "^15.5.10"
"@pubsweet/ui": "^9.1.3",
"@pubsweet/ui-toolkit": "^2.0.7",
"prop-types": "^15.5.10",
"query-string": "^5.0.0",
"react-router": "^4.2.0",
"styled-components": "^4.1.1"
},
"peerDependencies": {
"pubsweet-client": ">=1.0.0",
"pubsweet-server": ">=11.0.0",
"react": ">=16"
"react": ">=16",
"react-router-dom": "^4.3.1"
},
"repository": {
"type": "git",
"url": "https://gitlab.coko.foundation/pubsweet/pubsweet",
"path": "FormGroup"
"path": "PasswordResetFrontend"
}
}