From 9d2d63e40e8d4e27b06bd6ebefc5b02812b9e141 Mon Sep 17 00:00:00 2001
From: Alexandru Munteanu <alexandru.munt@gmail.com>
Date: Thu, 5 Jul 2018 15:51:07 +0300
Subject: [PATCH] feat(reset-password): implement the reset password flow

---
 .../components/SignUp/SignUpInvitationPage.js | 44 ++++++++-
 .../src/components/SignUp/SignUpStep1.js      | 93 +++++++++++++++----
 packages/xpub-faraday/app/routes.js           | 28 +++++-
 3 files changed, 140 insertions(+), 25 deletions(-)

diff --git a/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js b/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js
index dccbba131..b0f56d4f1 100644
--- a/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js
+++ b/packages/components-faraday/src/components/SignUp/SignUpInvitationPage.js
@@ -41,9 +41,30 @@ const signUpUser = history => (values, dispatch) =>
     })
     .catch(handleFormError)
 
+const resetUserPassword = history => ({ email }, dispatch) =>
+  create(`/users/forgot-password`, { email })
+    .then(r => {
+      // go to dedicated route
+      history.push('/')
+    })
+    .catch(handleFormError)
+
+const setNewPassword = history => ({ email, token, password }, dispatch) =>
+  create(`/users/reset-password`, { email, token, password })
+    .then(() => {
+      login(dispatch, { username: email, password }, history).then(() =>
+        history.push('/'),
+      )
+    })
+    .catch(handleFormError)
+
 export default compose(
   withJournal,
-  withState('step', 'changeStep', 0),
+  withState(
+    'step',
+    'changeStep',
+    ({ type }) => (type === 'forgotPassword' || type === 'setPassword' ? 1 : 0),
+  ),
   withProps(({ location }) => {
     const params = new URLSearchParams(location.search)
     const email = params.get('email') || ''
@@ -67,13 +88,26 @@ export default compose(
   withHandlers({
     nextStep: ({ changeStep }) => () => changeStep(step => step + 1),
     prevStep: ({ changeStep }) => () => changeStep(step => step - 1),
-    submitConfirmation: ({
+    confirmInvitation: ({
       initialValues: { email = '', token = '' },
       history,
     }) => confirmUser(email, token, history),
     signUp: ({ history }) => signUpUser(history),
+    forgotPassword: ({ history }) => resetUserPassword(history),
+    setNewPassword: ({ history }) => setNewPassword(history),
   }),
-  withProps(({ type, signUp, submitConfirmation }) => ({
-    onSubmit: type === 'signup' ? signUp : submitConfirmation,
-  })),
+  withProps(
+    ({ type, signUp, confirmInvitation, forgotPassword, setNewPassword }) => {
+      switch (type) {
+        case 'forgotPassword':
+          return { onSubmit: forgotPassword }
+        case 'signup':
+          return { onSubmit: signUp }
+        case 'setPassword':
+          return { onSubmit: setNewPassword }
+        default:
+          return { onSubmit: confirmInvitation }
+      }
+    },
+  ),
 )(SignUpInvitation)
diff --git a/packages/components-faraday/src/components/SignUp/SignUpStep1.js b/packages/components-faraday/src/components/SignUp/SignUpStep1.js
index 062cfadd9..f221cccae 100644
--- a/packages/components-faraday/src/components/SignUp/SignUpStep1.js
+++ b/packages/components-faraday/src/components/SignUp/SignUpStep1.js
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, { Fragment } from 'react'
 import { reduxForm } from 'redux-form'
 import { required } from 'xpub-validators'
 import { Button, ValidatedField, TextField } from '@pubsweet/ui'
@@ -11,20 +11,43 @@ const { Row, Err, Label, RowItem, FormContainer } = FormItems
 const PasswordField = input => <TextField {...input} type="password" />
 const EmailField = input => <TextField {...input} type="email" />
 
-const Step1 = ({ handleSubmit, error, type, prevStep, submitting }) => (
-  <FormContainer onSubmit={handleSubmit}>
-    {type === 'signup' && (
-      <Row>
-        <RowItem vertical>
-          <Label>Email</Label>
-          <ValidatedField
-            component={EmailField}
-            name="email"
-            validate={[required, emailValidator]}
-          />
-        </RowItem>
-      </Row>
-    )}
+const SignUpForm = () => (
+  <Fragment>
+    <Row>
+      <RowItem vertical>
+        <Label>Email</Label>
+        <ValidatedField
+          component={EmailField}
+          name="email"
+          validate={[required, emailValidator]}
+        />
+      </RowItem>
+    </Row>
+    <Row>
+      <RowItem vertical>
+        <Label>Password</Label>
+        <ValidatedField
+          component={PasswordField}
+          name="password"
+          validate={[required]}
+        />
+      </RowItem>
+    </Row>
+    <Row>
+      <RowItem vertical>
+        <Label>Confirm password</Label>
+        <ValidatedField
+          component={PasswordField}
+          name="confirmPassword"
+          validate={[required]}
+        />
+      </RowItem>
+    </Row>
+  </Fragment>
+)
+
+const InviteForm = () => (
+  <Fragment>
     <Row>
       <RowItem vertical>
         <Label>Password</Label>
@@ -45,6 +68,37 @@ const Step1 = ({ handleSubmit, error, type, prevStep, submitting }) => (
         />
       </RowItem>
     </Row>
+  </Fragment>
+)
+
+const ForgotEmailForm = () => (
+  <Fragment>
+    <Row>
+      <RowItem vertical>
+        <Label>Email</Label>
+        <ValidatedField
+          component={EmailField}
+          name="email"
+          validate={[required, emailValidator]}
+        />
+      </RowItem>
+    </Row>
+  </Fragment>
+)
+
+const withoutBack = ['forgotPassword', 'setPassword']
+const Step1 = ({
+  error,
+  prevStep,
+  submitting,
+  handleSubmit,
+  type = 'invite',
+}) => (
+  <FormContainer onSubmit={handleSubmit}>
+    {type === 'signup' && <SignUpForm />}
+    {type === 'setPassword' && <InviteForm />}
+    {type === 'forgotPassword' && <ForgotEmailForm />}
+    {type === 'invite' && <InviteForm />}
     {error && (
       <Row>
         <RowItem>
@@ -53,10 +107,13 @@ const Step1 = ({ handleSubmit, error, type, prevStep, submitting }) => (
       </Row>
     )}
     <Row />
+
     <Row>
-      <Button onClick={prevStep} type="button">
-        BACK
-      </Button>
+      {!withoutBack.includes(type) && (
+        <Button onClick={prevStep} type="button">
+          BACK
+        </Button>
+      )}
       <Button disabled={submitting} primary type="submit">
         CONFIRM
       </Button>
diff --git a/packages/xpub-faraday/app/routes.js b/packages/xpub-faraday/app/routes.js
index 4de1459d8..04c38a85b 100644
--- a/packages/xpub-faraday/app/routes.js
+++ b/packages/xpub-faraday/app/routes.js
@@ -26,7 +26,7 @@ import {
 
 import FaradayApp from './FaradayApp'
 
-const LoginPage = withProps({ passwordReset: false })(Login)
+const LoginPage = withProps({ passwordReset: true })(Login)
 
 const PrivateRoute = ({ component: Component, ...rest }) => (
   <Route
@@ -43,6 +43,7 @@ const Routes = () => (
   <FaradayApp>
     <Switch>
       <Route component={LoginPage} exact path="/login" />
+      <Route component={SignUpInvitationPage} exact path="/invite" />
       <Route
         component={routeParams => (
           <SignUpInvitationPage
@@ -55,6 +56,30 @@ const Routes = () => (
         exact
         path="/signup"
       />
+      <Route
+        component={routeParams => (
+          <SignUpInvitationPage
+            subtitle={null}
+            title="Reset password"
+            type="forgotPassword"
+            {...routeParams}
+          />
+        )}
+        exact
+        path="/password-reset"
+      />
+      <Route
+        component={routeParams => (
+          <SignUpInvitationPage
+            subtitle={null}
+            title="Set new password"
+            type="setPassword"
+            {...routeParams}
+          />
+        )}
+        exact
+        path="/forgot-password"
+      />
       <Route component={ConfirmAccount} exact path="/confirm-signup" />
       <PrivateRoute component={DashboardPage} exact path="/" />
       <PrivateRoute
@@ -75,7 +100,6 @@ const Routes = () => (
         exact
         path="/projects/:project/versions/:version/submit"
       />
-      <Route component={SignUpInvitationPage} exact path="/invite" />
       <Route component={ReviewerSignUp} exact path="/invite-reviewer" />
       <PrivateRoute
         component={ManuscriptPage}
-- 
GitLab