From 0a51cce555c69a24c0daa7f8be2ccd078e9c5f79 Mon Sep 17 00:00:00 2001
From: Bogdan Cochior <bogdan.cochior@thinslices.com>
Date: Wed, 11 Jul 2018 15:45:30 +0300
Subject: [PATCH] feat(change-password): implement change password UI form

---
 .../components/UserProfile/AccountDetails.js  |  3 +-
 .../UserProfile/AccountDetailsCard.js         |  5 +-
 .../UserProfile/ChangePasswordPage.js         | 98 +++++++++++++++++++
 .../components/UserProfile/UserProfilePage.js |  2 +-
 .../src/components/index.js                   |  1 +
 .../src/components/utils.js                   | 23 +++++
 packages/xpub-faraday/app/routes.js           | 10 +-
 7 files changed, 138 insertions(+), 4 deletions(-)
 create mode 100644 packages/components-faraday/src/components/UserProfile/ChangePasswordPage.js

diff --git a/packages/components-faraday/src/components/UserProfile/AccountDetails.js b/packages/components-faraday/src/components/UserProfile/AccountDetails.js
index 5932cb212..768c8a46e 100644
--- a/packages/components-faraday/src/components/UserProfile/AccountDetails.js
+++ b/packages/components-faraday/src/components/UserProfile/AccountDetails.js
@@ -6,7 +6,7 @@ import { compose, withHandlers, withState } from 'recompose'
 import AccountDetailsCard from './AccountDetailsCard'
 import AccountDetailsEdit from './AccountDetailsEdit'
 
-const AccountDetails = ({ user, isEdit, setEditMode, journal }) => (
+const AccountDetails = ({ user, isEdit, setEditMode, journal, history }) => (
   <Root>
     {isEdit ? (
       <AccountDetailsEdit
@@ -16,6 +16,7 @@ const AccountDetails = ({ user, isEdit, setEditMode, journal }) => (
       />
     ) : (
       <AccountDetailsCard
+        history={history}
         journal={journal}
         setEditMode={setEditMode}
         user={user}
diff --git a/packages/components-faraday/src/components/UserProfile/AccountDetailsCard.js b/packages/components-faraday/src/components/UserProfile/AccountDetailsCard.js
index db9172efa..22dadc2aa 100644
--- a/packages/components-faraday/src/components/UserProfile/AccountDetailsCard.js
+++ b/packages/components-faraday/src/components/UserProfile/AccountDetailsCard.js
@@ -13,6 +13,7 @@ import { getUserTitle } from '../utils'
 const AccountDetailsCard = ({
   user: { firstName = '', lastName = '', affiliation = '-', title = '' },
   journal,
+  history,
   setEditMode,
 }) => (
   <Fragment>
@@ -48,7 +49,9 @@ const AccountDetailsCard = ({
     <Row noMargin>
       <RowItem>
         <LinkText onClick={() => setEditMode(true)}>Edit details</LinkText>
-        <LinkText>Change Password</LinkText>
+        <LinkText onClick={() => history.push('/profile/change-password')}>
+          Change Password
+        </LinkText>
       </RowItem>
     </Row>
   </Fragment>
diff --git a/packages/components-faraday/src/components/UserProfile/ChangePasswordPage.js b/packages/components-faraday/src/components/UserProfile/ChangePasswordPage.js
new file mode 100644
index 000000000..8405473a8
--- /dev/null
+++ b/packages/components-faraday/src/components/UserProfile/ChangePasswordPage.js
@@ -0,0 +1,98 @@
+import React from 'react'
+import { compose } from 'recompose'
+import { connect } from 'react-redux'
+import { reduxForm } from 'redux-form'
+import styled from 'styled-components'
+import { required } from 'xpub-validators'
+import { selectCurrentUser } from 'xpub-selectors'
+import { Button, th, TextField, ValidatedField } from '@pubsweet/ui'
+import {
+  onSubmitChangePassword as onSubmit,
+  changePasswordValidator,
+} from '../utils'
+
+import { Row, RowItem, Label, Title, Err } from '../UIComponents/FormItems'
+
+const PasswordField = input => <TextField {...input} type="password" />
+
+const ChangePassword = ({ history, handleSubmit, error }) => (
+  <Root>
+    <Form onSubmit={handleSubmit}>
+      <Row>
+        <RowItem vertical>
+          <Title>Change Password</Title>
+        </RowItem>
+      </Row>
+      <Row>
+        <RowItem vertical>
+          <Label>Current Password *</Label>
+          <ValidatedField
+            component={PasswordField}
+            name="password"
+            validate={[required]}
+          />
+        </RowItem>
+      </Row>
+      <Row>
+        <RowItem vertical>
+          <Label>New Password *</Label>
+          <ValidatedField
+            component={PasswordField}
+            name="newPassword"
+            validate={[required]}
+          />
+        </RowItem>
+      </Row>
+      <Row>
+        <RowItem vertical>
+          <Label>Re-type password *</Label>
+          <ValidatedField
+            component={PasswordField}
+            name="confirmNewPassword"
+            validate={[required]}
+          />
+        </RowItem>
+      </Row>
+      {error && (
+        <Row>
+          <RowItem>
+            <Err>{error}</Err>
+          </RowItem>
+        </Row>
+      )}
+      <Row />
+      <Row>
+        <Button onClick={history.goBack}>Cancel</Button>
+        <Button primary type="submit">
+          Update
+        </Button>
+      </Row>
+    </Form>
+  </Root>
+)
+
+export default compose(
+  connect(state => ({
+    user: selectCurrentUser(state),
+  })),
+  reduxForm({
+    form: 'changePassword',
+    onSubmit,
+    validate: changePasswordValidator,
+  }),
+)(ChangePassword)
+
+// #region styles
+const Root = styled.div`
+  display: flex;
+  flex-direction: column;
+  max-width: 30em;
+  margin: 0 auto;
+`
+const Form = styled.form`
+  background-color: ${th('colorBackground')};
+  padding: ${th('gridUnit')};
+  border: ${th('borderDefault')};
+`
+
+// #endregion
diff --git a/packages/components-faraday/src/components/UserProfile/UserProfilePage.js b/packages/components-faraday/src/components/UserProfile/UserProfilePage.js
index 75f207cfb..64dd9d28e 100644
--- a/packages/components-faraday/src/components/UserProfile/UserProfilePage.js
+++ b/packages/components-faraday/src/components/UserProfile/UserProfilePage.js
@@ -19,7 +19,7 @@ const UserProfilePage = ({ history, user }) => (
       title="Account Settings"
       underlined
     />
-    <AccountDetails user={user} />
+    <AccountDetails history={history} user={user} />
     <EmailNotifications subscribed={get(user, 'subscription')} />
     <LinkOrcID orcid={get(user, 'orcid')} />
   </Root>
diff --git a/packages/components-faraday/src/components/index.js b/packages/components-faraday/src/components/index.js
index 54fdcb573..ddc29a62c 100644
--- a/packages/components-faraday/src/components/index.js
+++ b/packages/components-faraday/src/components/index.js
@@ -9,6 +9,7 @@ export { default as AuthorList } from './AuthorList/AuthorList'
 export { default as withVersion } from './Dashboard/withVersion.js'
 export { default as SortableList } from './SortableList/SortableList'
 export { default as UserProfilePage } from './UserProfile/UserProfilePage'
+export { default as ChangePasswordPage } from './UserProfile/ChangePasswordPage'
 
 export { Decision }
 export { Components }
diff --git a/packages/components-faraday/src/components/utils.js b/packages/components-faraday/src/components/utils.js
index e6dbf37df..e791983b4 100644
--- a/packages/components-faraday/src/components/utils.js
+++ b/packages/components-faraday/src/components/utils.js
@@ -98,6 +98,20 @@ export const passwordValidator = values => {
   return errors
 }
 
+export const changePasswordValidator = values => {
+  const errors = {}
+  if (!values.password) {
+    errors.password = 'Required'
+  }
+  if (!values.newPassword) {
+    errors.newPassword = 'Required'
+  } else if (values.newPassword !== values.confirmNewPassword) {
+    errors.confirmNewPassword = 'Password mismatched'
+  }
+
+  return errors
+}
+
 export const parseSearchParams = url => {
   const params = new URLSearchParams(url)
   const parsedObject = {}
@@ -124,6 +138,7 @@ export const parseUpdateUser = values => {
   return pick(values, valuesToSave)
 }
 
+// TODO: move to a dataservice
 export const onSubmitUser = (values, dispatch, { setEditMode }) =>
   update(`/users/${values.id}`, parseUpdateUser(values))
     .then(user => {
@@ -131,3 +146,11 @@ export const onSubmitUser = (values, dispatch, { setEditMode }) =>
       dispatch(actions.getCurrentUser())
     })
     .catch(handleFormError)
+
+// Temporary till API implementation
+export const onSubmitChangePassword = (values, dispatch, { history, user }) =>
+  update(`/users/${values.id}`, parseUpdateUser(values))
+    .then(() => {
+      history.goBack()
+    })
+    .catch(handleFormError)
diff --git a/packages/xpub-faraday/app/routes.js b/packages/xpub-faraday/app/routes.js
index 77dff3d32..826ed981b 100644
--- a/packages/xpub-faraday/app/routes.js
+++ b/packages/xpub-faraday/app/routes.js
@@ -3,7 +3,10 @@ import { Route, Switch } from 'react-router-dom'
 import { AuthenticatedComponent } from 'pubsweet-client'
 
 import { Wizard } from 'pubsweet-component-wizard/src/components'
-import { UserProfilePage } from 'pubsweet-components-faraday/src/components'
+import {
+  UserProfilePage,
+  ChangePasswordPage,
+} from 'pubsweet-components-faraday/src/components'
 import { ManuscriptPage } from 'pubsweet-component-manuscript/src/components'
 import DashboardPage from 'pubsweet-components-faraday/src/components/Dashboard'
 import LoginPage from 'pubsweet-components-faraday/src/components/Login/LoginPage'
@@ -83,6 +86,11 @@ const Routes = () => (
       <Route component={ConfirmAccount} exact path="/confirm-signup" />
       <PrivateRoute component={DashboardPage} exact path="/" />
       <PrivateRoute component={UserProfilePage} exact path="/profile" />
+      <PrivateRoute
+        component={ChangePasswordPage}
+        exact
+        path="/profile/change-password"
+      />
       <PrivateRoute
         component={ConfirmationPage}
         exact
-- 
GitLab