@graphcommerce/magento-customer 6.2.0-canary.43 → 6.2.0-canary.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/components/ChangePasswordForm/ChangePasswordForm.tsx +44 -30
- package/components/ResetPasswordForm/ResetPasswordForm.tsx +5 -9
- package/components/SignInForm/SignInForm.tsx +18 -11
- package/components/SignInForm/SignInFormInline.tsx +17 -17
- package/components/SignUpForm/SignUpForm.tsx +13 -27
- package/components/SignUpForm/SignUpFormInline.tsx +24 -41
- package/components/UpdateCustomerEmailForm/UpdateCustomerEmailForm.tsx +27 -20
- package/components/ValidatedPasswordElement/ValidatedPasswordElement.tsx +47 -0
- package/package.json +13 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 6.2.0-canary.45
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1962](https://github.com/graphcommerce-org/graphcommerce/pull/1962) [`518b6ca24`](https://github.com/graphcommerce-org/graphcommerce/commit/518b6ca248fc94624dc06eb02de5b3eac0fc9483) - Created a new `<ValidatedPasswordElement/>` which validates according to Magento's validation groups and implement on all locations. Move remaining password fields to `<PasswordElement />` ([@carlocarels90](https://github.com/carlocarels90))
|
|
8
|
+
|
|
9
|
+
## 6.2.0-canary.44
|
|
10
|
+
|
|
3
11
|
## 6.2.0-canary.43
|
|
4
12
|
|
|
5
13
|
## 6.2.0-canary.42
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ApolloErrorSnackbar,
|
|
3
|
+
PasswordElement,
|
|
4
|
+
PasswordRepeatElement,
|
|
5
|
+
} from '@graphcommerce/ecommerce-ui'
|
|
6
|
+
import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
|
|
1
7
|
import {
|
|
2
8
|
Form,
|
|
3
9
|
FormActions,
|
|
@@ -7,10 +13,8 @@ import {
|
|
|
7
13
|
Button,
|
|
8
14
|
} from '@graphcommerce/next-ui'
|
|
9
15
|
import { useFormGqlMutation } from '@graphcommerce/react-hook-form'
|
|
10
|
-
import { i18n } from '@lingui/core'
|
|
11
16
|
import { Trans } from '@lingui/react'
|
|
12
|
-
import {
|
|
13
|
-
import { ApolloCustomerErrorAlert } from '../ApolloCustomerError/ApolloCustomerErrorAlert'
|
|
17
|
+
import { ValidatedPasswordElement } from '../ValidatedPasswordElement/ValidatedPasswordElement'
|
|
14
18
|
import {
|
|
15
19
|
ChangePasswordDocument,
|
|
16
20
|
ChangePasswordMutation,
|
|
@@ -21,55 +25,61 @@ export function ChangePasswordForm() {
|
|
|
21
25
|
const form = useFormGqlMutation<
|
|
22
26
|
ChangePasswordMutation,
|
|
23
27
|
ChangePasswordMutationVariables & { confirmPassword?: string }
|
|
24
|
-
>(ChangePasswordDocument)
|
|
25
|
-
const {
|
|
28
|
+
>(ChangePasswordDocument, {}, { errorPolicy: 'all' })
|
|
29
|
+
const { handleSubmit, required, data, formState, error, control } = form
|
|
30
|
+
const [remainingError0, authenticationError] = graphqlErrorByCategory({
|
|
31
|
+
category: 'graphql-authentication',
|
|
32
|
+
error,
|
|
33
|
+
})
|
|
34
|
+
const [remainingError, inputError] = graphqlErrorByCategory({
|
|
35
|
+
category: 'graphql-input',
|
|
36
|
+
error: remainingError0,
|
|
37
|
+
})
|
|
38
|
+
|
|
26
39
|
const submitHandler = handleSubmit(() => {})
|
|
27
|
-
|
|
40
|
+
|
|
41
|
+
const showSuccess = !formState.isSubmitting && formState.isSubmitSuccessful && !error?.message
|
|
28
42
|
|
|
29
43
|
return (
|
|
30
44
|
<Form onSubmit={submitHandler} noValidate>
|
|
31
45
|
<FormRow>
|
|
32
|
-
<
|
|
46
|
+
<PasswordElement
|
|
47
|
+
control={control}
|
|
48
|
+
name='currentPassword'
|
|
33
49
|
variant='outlined'
|
|
34
|
-
|
|
35
|
-
error={!!formState.errors.currentPassword}
|
|
50
|
+
autoComplete='current-password'
|
|
36
51
|
label={<Trans id='Current Password' />}
|
|
37
52
|
required={required.currentPassword}
|
|
38
|
-
{...muiRegister('currentPassword', { required: required.currentPassword })}
|
|
39
|
-
helperText={formState.errors.currentPassword?.message}
|
|
40
53
|
disabled={formState.isSubmitting}
|
|
54
|
+
error={Boolean(authenticationError)}
|
|
55
|
+
helperText={authenticationError?.message}
|
|
41
56
|
/>
|
|
42
57
|
</FormRow>
|
|
43
58
|
|
|
44
59
|
<FormRow>
|
|
45
|
-
<
|
|
60
|
+
<ValidatedPasswordElement
|
|
61
|
+
control={control}
|
|
62
|
+
name='newPassword'
|
|
46
63
|
variant='outlined'
|
|
47
|
-
|
|
48
|
-
error={!!formState.errors.newPassword}
|
|
64
|
+
autoComplete='new-password'
|
|
49
65
|
label={<Trans id='New password' />}
|
|
50
66
|
required={required.newPassword}
|
|
51
|
-
{...muiRegister('newPassword', { required: required.newPassword })}
|
|
52
|
-
helperText={formState.errors.newPassword?.message}
|
|
53
67
|
disabled={formState.isSubmitting}
|
|
68
|
+
error={Boolean(inputError)}
|
|
69
|
+
helperText={inputError?.message}
|
|
54
70
|
/>
|
|
55
|
-
|
|
56
|
-
|
|
71
|
+
<PasswordRepeatElement
|
|
72
|
+
control={control}
|
|
73
|
+
name='confirmPassword'
|
|
74
|
+
passwordFieldName='newPassword'
|
|
75
|
+
autoComplete='new-password'
|
|
57
76
|
variant='outlined'
|
|
58
|
-
type='password'
|
|
59
|
-
error={!!formState.errors.confirmPassword}
|
|
60
77
|
label={<Trans id='Confirm password' />}
|
|
61
78
|
required
|
|
62
|
-
{...muiRegister('confirmPassword', {
|
|
63
|
-
required: true,
|
|
64
|
-
validate: (value) => value === pass || i18n._(/* i18n */ "Passwords don't match"),
|
|
65
|
-
})}
|
|
66
|
-
helperText={formState.errors.confirmPassword?.message}
|
|
67
79
|
disabled={formState.isSubmitting}
|
|
68
80
|
/>
|
|
69
81
|
</FormRow>
|
|
70
82
|
|
|
71
|
-
<ApolloCustomerErrorAlert error={error} />
|
|
72
|
-
|
|
73
83
|
<FormDivider />
|
|
74
84
|
|
|
75
85
|
<FormActions>
|
|
@@ -84,9 +94,13 @@ export function ChangePasswordForm() {
|
|
|
84
94
|
</Button>
|
|
85
95
|
</FormActions>
|
|
86
96
|
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
<ApolloErrorSnackbar error={remainingError} />
|
|
98
|
+
|
|
99
|
+
{showSuccess && (
|
|
100
|
+
<MessageSnackbar open={showSuccess} sticky variant='pill'>
|
|
101
|
+
<Trans id='Successfully changed password' />
|
|
102
|
+
</MessageSnackbar>
|
|
103
|
+
)}
|
|
90
104
|
</Form>
|
|
91
105
|
)
|
|
92
106
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
PasswordElement,
|
|
3
|
-
PasswordRepeatElement,
|
|
4
|
-
TextFieldElement,
|
|
5
|
-
} from '@graphcommerce/ecommerce-ui'
|
|
1
|
+
import { PasswordRepeatElement, TextFieldElement } from '@graphcommerce/ecommerce-ui'
|
|
6
2
|
import { Button, Form, FormActions, FormRow } from '@graphcommerce/next-ui'
|
|
7
3
|
import { useFormGqlMutation } from '@graphcommerce/react-hook-form'
|
|
8
4
|
import { Trans } from '@lingui/react'
|
|
9
|
-
import { TextField } from '@mui/material'
|
|
10
5
|
import { useRouter } from 'next/router'
|
|
11
6
|
import { ApolloCustomerErrorAlert } from '../ApolloCustomerError/ApolloCustomerErrorAlert'
|
|
7
|
+
import { ValidatedPasswordElement } from '../ValidatedPasswordElement/ValidatedPasswordElement'
|
|
12
8
|
import {
|
|
13
9
|
ResetPasswordDocument,
|
|
14
10
|
ResetPasswordMutation,
|
|
@@ -59,11 +55,11 @@ export function ResetPasswordForm(props: ResetPasswordFormProps) {
|
|
|
59
55
|
/>
|
|
60
56
|
</FormRow>
|
|
61
57
|
<FormRow>
|
|
62
|
-
<
|
|
58
|
+
<ValidatedPasswordElement
|
|
63
59
|
control={control}
|
|
64
60
|
name='newPassword'
|
|
61
|
+
autoComplete='new-password'
|
|
65
62
|
variant='outlined'
|
|
66
|
-
type='password'
|
|
67
63
|
label={<Trans id='New password' />}
|
|
68
64
|
required
|
|
69
65
|
disabled={formState.isSubmitting}
|
|
@@ -71,9 +67,9 @@ export function ResetPasswordForm(props: ResetPasswordFormProps) {
|
|
|
71
67
|
<PasswordRepeatElement
|
|
72
68
|
control={control}
|
|
73
69
|
name='confirmPassword'
|
|
70
|
+
autoComplete='new-password'
|
|
74
71
|
passwordFieldName='newPassword'
|
|
75
72
|
variant='outlined'
|
|
76
|
-
type='password'
|
|
77
73
|
label={<Trans id='Confirm password' />}
|
|
78
74
|
required
|
|
79
75
|
disabled={formState.isSubmitting}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
import { PasswordElement } from '@graphcommerce/ecommerce-ui'
|
|
1
2
|
import { useApolloClient } from '@graphcommerce/graphql'
|
|
2
3
|
import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
|
|
3
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
FormRow,
|
|
7
|
+
FormActions,
|
|
8
|
+
iconEyeCrossed,
|
|
9
|
+
iconEye,
|
|
10
|
+
IconSvg,
|
|
11
|
+
} from '@graphcommerce/next-ui'
|
|
4
12
|
import { useFormGqlMutation } from '@graphcommerce/react-hook-form'
|
|
5
13
|
import { Trans } from '@lingui/react'
|
|
6
|
-
import { Box, FormControl, Link, SxProps,
|
|
14
|
+
import { Box, FormControl, IconButton, InputAdornment, Link, SxProps, Theme } from '@mui/material'
|
|
15
|
+
import { MouseEvent, useState } from 'react'
|
|
7
16
|
import { CustomerDocument } from '../../hooks'
|
|
8
17
|
import { ApolloCustomerErrorAlert } from '../ApolloCustomerError/ApolloCustomerErrorAlert'
|
|
9
18
|
import { SignInDocument } from './SignIn.gql'
|
|
@@ -12,7 +21,6 @@ type SignInFormProps = { email: string; sx?: SxProps<Theme> }
|
|
|
12
21
|
|
|
13
22
|
export function SignInForm(props: SignInFormProps) {
|
|
14
23
|
const { email, sx } = props
|
|
15
|
-
|
|
16
24
|
const client = useApolloClient()
|
|
17
25
|
const form = useFormGqlMutation(
|
|
18
26
|
SignInDocument,
|
|
@@ -32,7 +40,7 @@ export function SignInForm(props: SignInFormProps) {
|
|
|
32
40
|
{ errorPolicy: 'all' },
|
|
33
41
|
)
|
|
34
42
|
|
|
35
|
-
const {
|
|
43
|
+
const { handleSubmit, required, formState, error, control } = form
|
|
36
44
|
const [remainingError, authError] = graphqlErrorByCategory({
|
|
37
45
|
category: 'graphql-authentication',
|
|
38
46
|
error,
|
|
@@ -41,18 +49,19 @@ export function SignInForm(props: SignInFormProps) {
|
|
|
41
49
|
|
|
42
50
|
return (
|
|
43
51
|
<Box component='form' onSubmit={submitHandler} noValidate sx={sx}>
|
|
44
|
-
<FormRow>
|
|
45
|
-
<
|
|
46
|
-
key='password'
|
|
52
|
+
<FormRow sx={{ gridTemplateColumns: 'none' }}>
|
|
53
|
+
<PasswordElement
|
|
47
54
|
variant='outlined'
|
|
48
|
-
type='password'
|
|
49
55
|
error={!!formState.errors.password || !!authError}
|
|
56
|
+
control={control}
|
|
57
|
+
name='password'
|
|
50
58
|
label={<Trans id='Password' />}
|
|
51
59
|
autoFocus
|
|
52
60
|
autoComplete='current-password'
|
|
53
61
|
id='current-password'
|
|
54
62
|
required={required.password}
|
|
55
|
-
{
|
|
63
|
+
disabled={formState.isSubmitting}
|
|
64
|
+
helperText={!!formState.errors.password || authError?.message}
|
|
56
65
|
InputProps={{
|
|
57
66
|
endAdornment: (
|
|
58
67
|
<Link href='/account/forgot-password' underline='hover' sx={{ whiteSpace: 'nowrap' }}>
|
|
@@ -60,8 +69,6 @@ export function SignInForm(props: SignInFormProps) {
|
|
|
60
69
|
</Link>
|
|
61
70
|
),
|
|
62
71
|
}}
|
|
63
|
-
helperText={formState.errors.password?.message || authError?.message}
|
|
64
|
-
disabled={formState.isSubmitting}
|
|
65
72
|
/>
|
|
66
73
|
</FormRow>
|
|
67
74
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { PasswordElement } from '@graphcommerce/ecommerce-ui'
|
|
1
2
|
import { Button, extendableComponent } from '@graphcommerce/next-ui'
|
|
2
3
|
import { useFormGqlMutation } from '@graphcommerce/react-hook-form'
|
|
3
4
|
import { Trans } from '@lingui/react'
|
|
4
|
-
import { Box,
|
|
5
|
+
import { Box, Link, SxProps, Theme } from '@mui/material'
|
|
5
6
|
import { SignInDocument, SignInMutationVariables } from './SignIn.gql'
|
|
6
7
|
|
|
7
8
|
type InlineSignInFormProps = Omit<SignInMutationVariables, 'password'> & {
|
|
@@ -12,13 +13,13 @@ type InlineSignInFormProps = Omit<SignInMutationVariables, 'password'> & {
|
|
|
12
13
|
const { classes } = extendableComponent('SignInFormInline', ['form', 'button'] as const)
|
|
13
14
|
|
|
14
15
|
export function SignInFormInline(props: InlineSignInFormProps) {
|
|
15
|
-
const { email, sx = [] } = props
|
|
16
|
+
const { email, children, sx = [] } = props
|
|
16
17
|
const form = useFormGqlMutation(
|
|
17
18
|
SignInDocument,
|
|
18
19
|
{ defaultValues: { email }, onBeforeSubmit: (values) => ({ ...values, email }) },
|
|
19
20
|
{ errorPolicy: 'all' },
|
|
20
21
|
)
|
|
21
|
-
const {
|
|
22
|
+
const { handleSubmit, required, formState, control } = form
|
|
22
23
|
const submitHandler = handleSubmit(() => {})
|
|
23
24
|
|
|
24
25
|
return (
|
|
@@ -41,35 +42,34 @@ export function SignInFormInline(props: InlineSignInFormProps) {
|
|
|
41
42
|
...(Array.isArray(sx) ? sx : [sx]),
|
|
42
43
|
]}
|
|
43
44
|
>
|
|
44
|
-
<
|
|
45
|
+
<PasswordElement
|
|
46
|
+
control={control}
|
|
45
47
|
variant='outlined'
|
|
46
|
-
|
|
47
|
-
error={!!formState.errors.password || !!error?.message}
|
|
48
|
+
name='password'
|
|
48
49
|
label={<Trans id='Password' />}
|
|
49
50
|
autoFocus
|
|
50
51
|
autoComplete='current-password'
|
|
51
52
|
id='current-password'
|
|
52
53
|
required={required.password}
|
|
53
|
-
{...muiRegister('password', { required: required.password })}
|
|
54
|
-
helperText={error?.message}
|
|
55
54
|
disabled={formState.isSubmitting}
|
|
56
55
|
InputProps={{
|
|
57
56
|
endAdornment: (
|
|
58
|
-
<
|
|
59
|
-
href='/account/forgot-password'
|
|
60
|
-
color='secondary'
|
|
61
|
-
variant='text'
|
|
62
|
-
className={classes.button}
|
|
63
|
-
sx={{ minWidth: 'max-content' }}
|
|
64
|
-
>
|
|
57
|
+
<Link href='/account/forgot-password' underline='hover' sx={{ whiteSpace: 'nowrap' }}>
|
|
65
58
|
<Trans id='Forgot password?' />
|
|
66
|
-
</
|
|
59
|
+
</Link>
|
|
67
60
|
),
|
|
68
61
|
}}
|
|
69
62
|
/>
|
|
70
|
-
<Button
|
|
63
|
+
<Button
|
|
64
|
+
type='submit'
|
|
65
|
+
loading={formState.isSubmitting}
|
|
66
|
+
color='secondary'
|
|
67
|
+
variant='pill'
|
|
68
|
+
sx={{ alignSelf: 'start', marginTop: (theme) => `calc(${theme.spacings.xxs} * .33)` }}
|
|
69
|
+
>
|
|
71
70
|
<Trans id='Sign in' />
|
|
72
71
|
</Button>
|
|
72
|
+
{children}
|
|
73
73
|
</Box>
|
|
74
74
|
)
|
|
75
75
|
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PasswordElement, PasswordRepeatElement } from '@graphcommerce/ecommerce-ui'
|
|
2
2
|
import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
|
|
3
|
-
import { StoreConfigDocument } from '@graphcommerce/magento-store'
|
|
4
3
|
import { Button, FormActions, FormRow } from '@graphcommerce/next-ui'
|
|
5
4
|
import { useFormGqlMutation, useFormPersist } from '@graphcommerce/react-hook-form'
|
|
6
|
-
import { i18n } from '@lingui/core'
|
|
7
5
|
import { Trans } from '@lingui/react'
|
|
8
|
-
import { Alert, FormControlLabel, Switch
|
|
6
|
+
import { Alert, FormControlLabel, Switch } from '@mui/material'
|
|
9
7
|
import { ApolloCustomerErrorSnackbar } from '../ApolloCustomerError/ApolloCustomerErrorSnackbar'
|
|
10
8
|
import { NameFields } from '../NameFields/NameFields'
|
|
11
9
|
import { SignUpDocument, SignUpMutation, SignUpMutationVariables } from './SignUp.gql'
|
|
12
10
|
import { SignUpConfirmDocument } from './SignUpConfirm.gql'
|
|
11
|
+
import { ValidatedPasswordElement } from '../ValidatedPasswordElement/ValidatedPasswordElement'
|
|
13
12
|
|
|
14
13
|
type SignUpFormProps = { email: string }
|
|
15
14
|
|
|
@@ -18,8 +17,6 @@ const requireEmailValidation = import.meta.graphCommerce.customerRequireEmailCon
|
|
|
18
17
|
export function SignUpForm(props: SignUpFormProps) {
|
|
19
18
|
const { email } = props
|
|
20
19
|
|
|
21
|
-
const storeConfig = useQuery(StoreConfigDocument).data?.storeConfig
|
|
22
|
-
|
|
23
20
|
const Mutation = requireEmailValidation ? SignUpConfirmDocument : SignUpDocument
|
|
24
21
|
|
|
25
22
|
const form = useFormGqlMutation<
|
|
@@ -31,11 +28,10 @@ export function SignUpForm(props: SignUpFormProps) {
|
|
|
31
28
|
{ errorPolicy: 'all' },
|
|
32
29
|
)
|
|
33
30
|
|
|
34
|
-
const { muiRegister, handleSubmit, required,
|
|
31
|
+
const { muiRegister, handleSubmit, required, formState, error, control } = form
|
|
35
32
|
const [remainingError, inputError] = graphqlErrorByCategory({ category: 'graphql-input', error })
|
|
36
33
|
|
|
37
34
|
const submitHandler = handleSubmit(() => {})
|
|
38
|
-
const watchPassword = watch('password')
|
|
39
35
|
|
|
40
36
|
useFormPersist({ form, name: 'SignUp', exclude: ['password', 'confirmPassword'] })
|
|
41
37
|
|
|
@@ -50,37 +46,27 @@ export function SignUpForm(props: SignUpFormProps) {
|
|
|
50
46
|
return (
|
|
51
47
|
<form onSubmit={submitHandler} noValidate>
|
|
52
48
|
<FormRow>
|
|
53
|
-
<
|
|
49
|
+
<ValidatedPasswordElement
|
|
50
|
+
control={control}
|
|
51
|
+
name='password'
|
|
54
52
|
variant='outlined'
|
|
55
|
-
type='password'
|
|
56
53
|
error={!!formState.errors.password || !!inputError}
|
|
57
54
|
label={<Trans id='Password' />}
|
|
58
55
|
autoFocus
|
|
59
56
|
autoComplete='new-password'
|
|
60
57
|
required={required.password}
|
|
61
|
-
{...muiRegister('password', {
|
|
62
|
-
required: required.password,
|
|
63
|
-
minLength: {
|
|
64
|
-
value: Number(storeConfig?.minimum_password_length ?? 8),
|
|
65
|
-
message: i18n._(/* i18n */ 'Password must have at least 8 characters'),
|
|
66
|
-
},
|
|
67
|
-
})}
|
|
68
|
-
helperText={formState.errors.password?.message || inputError?.message}
|
|
69
58
|
disabled={formState.isSubmitting}
|
|
59
|
+
helperText={inputError?.message}
|
|
70
60
|
/>
|
|
71
|
-
<
|
|
61
|
+
<PasswordRepeatElement
|
|
62
|
+
control={control}
|
|
63
|
+
name='confirmPassword'
|
|
64
|
+
passwordFieldName='password'
|
|
72
65
|
variant='outlined'
|
|
73
|
-
|
|
74
|
-
error={!!formState.errors.confirmPassword}
|
|
66
|
+
error={!!formState.errors.confirmPassword || !!inputError}
|
|
75
67
|
label={<Trans id='Confirm password' />}
|
|
76
68
|
autoComplete='new-password'
|
|
77
69
|
required
|
|
78
|
-
{...muiRegister('confirmPassword', {
|
|
79
|
-
required: true,
|
|
80
|
-
validate: (value) =>
|
|
81
|
-
value === watchPassword || i18n._(/* i18n */ "Passwords don't match"),
|
|
82
|
-
})}
|
|
83
|
-
helperText={formState.errors.confirmPassword?.message}
|
|
84
70
|
disabled={formState.isSubmitting}
|
|
85
71
|
/>
|
|
86
72
|
</FormRow>
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
ApolloErrorAlert,
|
|
3
|
+
PasswordElement,
|
|
4
|
+
PasswordRepeatElement,
|
|
5
|
+
} from '@graphcommerce/ecommerce-ui'
|
|
6
|
+
import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
|
|
4
7
|
import { Button, extendableComponent, Form, FormRow } from '@graphcommerce/next-ui'
|
|
5
8
|
import { useFormGqlMutation } from '@graphcommerce/react-hook-form'
|
|
6
|
-
import { i18n } from '@lingui/core'
|
|
7
9
|
import { Trans } from '@lingui/react'
|
|
8
|
-
import { Alert, Box
|
|
10
|
+
import { Alert, Box } from '@mui/material'
|
|
9
11
|
import React from 'react'
|
|
10
12
|
import { SignUpMutationVariables, SignUpMutation, SignUpDocument } from './SignUp.gql'
|
|
11
13
|
import { SignUpConfirmDocument } from './SignUpConfirm.gql'
|
|
14
|
+
import { ValidatedPasswordElement } from '../ValidatedPasswordElement/ValidatedPasswordElement'
|
|
12
15
|
|
|
13
16
|
type SignUpFormInlineProps = Pick<SignUpMutationVariables, 'email'> & {
|
|
14
17
|
children?: React.ReactNode
|
|
@@ -26,13 +29,8 @@ const { classes } = extendableComponent('SignUpFormInline', [
|
|
|
26
29
|
|
|
27
30
|
const requireEmailValidation = import.meta.graphCommerce.customerRequireEmailConfirmation ?? false
|
|
28
31
|
|
|
29
|
-
export function SignUpFormInline({
|
|
30
|
-
email,
|
|
31
|
-
children,
|
|
32
|
-
firstname,
|
|
33
|
-
lastname,
|
|
34
|
-
onSubmitted = () => {},
|
|
35
|
-
}: SignUpFormInlineProps) {
|
|
32
|
+
export function SignUpFormInline(props: SignUpFormInlineProps) {
|
|
33
|
+
const { email, children, firstname, lastname, onSubmitted = () => {} } = props
|
|
36
34
|
const Mutation = requireEmailValidation ? SignUpConfirmDocument : SignUpDocument
|
|
37
35
|
|
|
38
36
|
const form = useFormGqlMutation<
|
|
@@ -56,13 +54,9 @@ export function SignUpFormInline({
|
|
|
56
54
|
{ errorPolicy: 'all' },
|
|
57
55
|
)
|
|
58
56
|
|
|
59
|
-
const {
|
|
57
|
+
const { handleSubmit, formState, control, error, required } = form
|
|
58
|
+
const [remainingError, inputError] = graphqlErrorByCategory({ category: 'graphql-input', error })
|
|
60
59
|
const submitHandler = handleSubmit(() => {})
|
|
61
|
-
const watchPassword = watch('password')
|
|
62
|
-
|
|
63
|
-
const minPasswordLength = Number(
|
|
64
|
-
useQuery(StoreConfigDocument).data?.storeConfig?.minimum_password_length ?? 8,
|
|
65
|
-
)
|
|
66
60
|
|
|
67
61
|
if (requireEmailValidation && form.formState.isSubmitSuccessful) {
|
|
68
62
|
return (
|
|
@@ -75,36 +69,25 @@ export function SignUpFormInline({
|
|
|
75
69
|
return (
|
|
76
70
|
<Form onSubmit={submitHandler} noValidate className={classes.form} sx={{ padding: 0 }}>
|
|
77
71
|
<FormRow className={classes.row} sx={{ padding: 0 }}>
|
|
78
|
-
<
|
|
72
|
+
<ValidatedPasswordElement
|
|
73
|
+
control={control}
|
|
74
|
+
name='password'
|
|
75
|
+
autoComplete='new-password'
|
|
79
76
|
variant='outlined'
|
|
80
|
-
type='password'
|
|
81
|
-
error={!!formState.errors.password || !!form.error}
|
|
82
77
|
label={<Trans id='Password' />}
|
|
83
|
-
autoFocus
|
|
84
|
-
autoComplete='new-password'
|
|
85
|
-
id='new-password'
|
|
86
78
|
required={required.password}
|
|
87
|
-
{...muiRegister('password', {
|
|
88
|
-
required: required.password,
|
|
89
|
-
minLength: {
|
|
90
|
-
value: minPasswordLength,
|
|
91
|
-
message: i18n._(/* i18n */ 'Password must have at least 8 characters'),
|
|
92
|
-
},
|
|
93
|
-
})}
|
|
94
79
|
disabled={formState.isSubmitting}
|
|
80
|
+
error={!!inputError}
|
|
81
|
+
helperText={inputError?.message}
|
|
95
82
|
/>
|
|
96
|
-
<
|
|
83
|
+
<PasswordRepeatElement
|
|
84
|
+
control={control}
|
|
85
|
+
name='confirmPassword'
|
|
86
|
+
passwordFieldName='password'
|
|
87
|
+
autoComplete='new-password'
|
|
97
88
|
variant='outlined'
|
|
98
|
-
type='password'
|
|
99
|
-
error={!!formState.errors.confirmPassword || !!form.error}
|
|
100
89
|
label={<Trans id='Confirm password' />}
|
|
101
|
-
autoComplete='new-password'
|
|
102
90
|
required
|
|
103
|
-
{...muiRegister('confirmPassword', {
|
|
104
|
-
required: true,
|
|
105
|
-
validate: (value) => value === watchPassword,
|
|
106
|
-
})}
|
|
107
|
-
helperText={!!formState.errors.confirmPassword && <Trans id='Passwords should match' />}
|
|
108
91
|
disabled={formState.isSubmitting}
|
|
109
92
|
/>
|
|
110
93
|
</FormRow>
|
|
@@ -133,7 +116,7 @@ export function SignUpFormInline({
|
|
|
133
116
|
</Box>
|
|
134
117
|
</FormRow>
|
|
135
118
|
</FormRow>
|
|
136
|
-
<ApolloErrorAlert error={
|
|
119
|
+
<ApolloErrorAlert error={remainingError} />
|
|
137
120
|
</Form>
|
|
138
121
|
)
|
|
139
122
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PasswordElement } from '@graphcommerce/ecommerce-ui'
|
|
1
2
|
import {
|
|
2
3
|
Button,
|
|
3
4
|
Form,
|
|
@@ -16,6 +17,8 @@ import {
|
|
|
16
17
|
UpdateCustomerEmailMutation,
|
|
17
18
|
UpdateCustomerEmailMutationVariables,
|
|
18
19
|
} from './UpdateCustomerEmail.gql'
|
|
20
|
+
import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
|
|
21
|
+
import { ApolloCustomerErrorSnackbar } from '../ApolloCustomerError'
|
|
19
22
|
|
|
20
23
|
type UpdateCustomerEmailFormProps = {
|
|
21
24
|
email: string
|
|
@@ -26,13 +29,20 @@ export function UpdateCustomerEmailForm(props: UpdateCustomerEmailFormProps) {
|
|
|
26
29
|
|
|
27
30
|
const form = useFormGqlMutation<
|
|
28
31
|
UpdateCustomerEmailMutation,
|
|
29
|
-
UpdateCustomerEmailMutationVariables & {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
32
|
+
UpdateCustomerEmailMutationVariables & { currentEmail?: string; confirmEmail?: string }
|
|
33
|
+
>(
|
|
34
|
+
UpdateCustomerEmailDocument,
|
|
35
|
+
{},
|
|
36
|
+
{
|
|
37
|
+
errorPolicy: 'all',
|
|
38
|
+
},
|
|
39
|
+
)
|
|
34
40
|
|
|
35
|
-
const { handleSubmit, error, required, formState, watch, muiRegister, reset } = form
|
|
41
|
+
const { handleSubmit, error, required, formState, watch, muiRegister, reset, control } = form
|
|
42
|
+
const [remainingError, authenticationError] = graphqlErrorByCategory({
|
|
43
|
+
category: 'graphql-authentication',
|
|
44
|
+
error,
|
|
45
|
+
})
|
|
36
46
|
const submit = handleSubmit(() => {
|
|
37
47
|
reset()
|
|
38
48
|
})
|
|
@@ -45,7 +55,7 @@ export function UpdateCustomerEmailForm(props: UpdateCustomerEmailFormProps) {
|
|
|
45
55
|
key='current-email'
|
|
46
56
|
variant='outlined'
|
|
47
57
|
type='text'
|
|
48
|
-
autoComplete='
|
|
58
|
+
autoComplete='email'
|
|
49
59
|
autoFocus
|
|
50
60
|
error={formState.isSubmitted && !!formState.errors.currentEmail}
|
|
51
61
|
helperText={formState.isSubmitted && formState.errors.currentEmail?.message}
|
|
@@ -67,8 +77,7 @@ export function UpdateCustomerEmailForm(props: UpdateCustomerEmailFormProps) {
|
|
|
67
77
|
key='email'
|
|
68
78
|
variant='outlined'
|
|
69
79
|
type='text'
|
|
70
|
-
autoComplete='
|
|
71
|
-
autoFocus
|
|
80
|
+
autoComplete='off'
|
|
72
81
|
error={formState.isSubmitted && !!formState.errors.email}
|
|
73
82
|
helperText={formState.isSubmitted && formState.errors.email?.message}
|
|
74
83
|
label={<Trans id='New email' />}
|
|
@@ -82,8 +91,7 @@ export function UpdateCustomerEmailForm(props: UpdateCustomerEmailFormProps) {
|
|
|
82
91
|
key='confirm-email'
|
|
83
92
|
variant='outlined'
|
|
84
93
|
type='text'
|
|
85
|
-
autoComplete='
|
|
86
|
-
autoFocus
|
|
94
|
+
autoComplete='off'
|
|
87
95
|
error={formState.isSubmitted && !!formState.errors.confirmEmail}
|
|
88
96
|
helperText={formState.isSubmitted && formState.errors.confirmEmail?.message}
|
|
89
97
|
label={<Trans id='Confirm new email' />}
|
|
@@ -96,21 +104,21 @@ export function UpdateCustomerEmailForm(props: UpdateCustomerEmailFormProps) {
|
|
|
96
104
|
</FormRow>
|
|
97
105
|
|
|
98
106
|
<FormRow>
|
|
99
|
-
<
|
|
107
|
+
<PasswordElement
|
|
108
|
+
control={control}
|
|
100
109
|
variant='outlined'
|
|
101
|
-
|
|
102
|
-
error={!!formState.errors.password}
|
|
110
|
+
name='password'
|
|
103
111
|
label={<Trans id='Password' />}
|
|
104
|
-
autoComplete='password'
|
|
112
|
+
autoComplete='current-password'
|
|
105
113
|
required={required.password}
|
|
106
|
-
{...muiRegister('password', {
|
|
107
|
-
required: required.password,
|
|
108
|
-
})}
|
|
109
|
-
helperText={formState.errors.password?.message}
|
|
110
114
|
disabled={formState.isSubmitting}
|
|
115
|
+
error={Boolean(authenticationError)}
|
|
116
|
+
helperText={authenticationError?.message}
|
|
111
117
|
/>
|
|
112
118
|
</FormRow>
|
|
113
119
|
|
|
120
|
+
<ApolloCustomerErrorSnackbar error={remainingError} />
|
|
121
|
+
|
|
114
122
|
<FormDivider />
|
|
115
123
|
<FormActions>
|
|
116
124
|
<Button
|
|
@@ -123,7 +131,6 @@ export function UpdateCustomerEmailForm(props: UpdateCustomerEmailFormProps) {
|
|
|
123
131
|
<Trans id='Save changes' />
|
|
124
132
|
</Button>
|
|
125
133
|
</FormActions>
|
|
126
|
-
<ApolloCustomerErrorAlert error={error} />
|
|
127
134
|
|
|
128
135
|
<MessageSnackbar sticky open={formState.isSubmitSuccessful && !error}>
|
|
129
136
|
<Trans id='Successfully updated email' />
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { PasswordElement, PasswordElementProps } from '@graphcommerce/ecommerce-ui'
|
|
2
|
+
import { useQuery } from '@graphcommerce/graphql'
|
|
3
|
+
import { StoreConfigDocument } from '@graphcommerce/magento-store'
|
|
4
|
+
import { FieldValues } from '@graphcommerce/react-hook-form'
|
|
5
|
+
import { i18n } from '@lingui/core'
|
|
6
|
+
|
|
7
|
+
export type ValidatedPasswordElementProps<T extends FieldValues> = PasswordElementProps<T>
|
|
8
|
+
|
|
9
|
+
export function ValidatedPasswordElement<TFieldValues extends FieldValues>(
|
|
10
|
+
props: PasswordElementProps<TFieldValues>,
|
|
11
|
+
): JSX.Element {
|
|
12
|
+
const { ...textFieldProps } = props
|
|
13
|
+
|
|
14
|
+
const storeConfig = useQuery(StoreConfigDocument).data?.storeConfig
|
|
15
|
+
const minPasswordLength = Number(storeConfig?.minimum_password_length) ?? 0
|
|
16
|
+
const passwordMinCharacterSets = Number(storeConfig?.required_character_classes_number) ?? 0
|
|
17
|
+
|
|
18
|
+
const validation: NonNullable<PasswordElementProps<TFieldValues>['validation']> = {}
|
|
19
|
+
|
|
20
|
+
validation.minLength = {
|
|
21
|
+
value: minPasswordLength,
|
|
22
|
+
message: i18n._(/* i18n */ 'Password must have at least {minPasswordLength} characters', {
|
|
23
|
+
minPasswordLength,
|
|
24
|
+
}),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
validation.validate = (value: string) => {
|
|
28
|
+
const pass = value.trim()
|
|
29
|
+
let counter = 0
|
|
30
|
+
|
|
31
|
+
if (pass.match(/\d+/)) counter++
|
|
32
|
+
if (pass.match(/[a-z]+/)) counter++
|
|
33
|
+
if (pass.match(/[A-Z]+/)) counter++
|
|
34
|
+
if (pass.match(/[^a-zA-Z0-9]+/)) counter++
|
|
35
|
+
|
|
36
|
+
if (counter < passwordMinCharacterSets) {
|
|
37
|
+
return i18n._(
|
|
38
|
+
/* i18n */ 'Minimum of different classes of characters in password is {passwordMinCharacterSets}. Classes of characters: Lower Case, Upper Case, Digits, Special Characters.',
|
|
39
|
+
{ passwordMinCharacterSets },
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return <PasswordElement {...textFieldProps} validation={validation} />
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graphcommerce/magento-customer",
|
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
|
5
|
-
"version": "6.2.0-canary.
|
|
5
|
+
"version": "6.2.0-canary.45",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
8
8
|
"eslintConfig": {
|
|
@@ -12,20 +12,20 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@graphcommerce/eslint-config-pwa": "6.2.0-canary.
|
|
16
|
-
"@graphcommerce/prettier-config-pwa": "6.2.0-canary.
|
|
17
|
-
"@graphcommerce/typescript-config-pwa": "6.2.0-canary.
|
|
15
|
+
"@graphcommerce/eslint-config-pwa": "6.2.0-canary.45",
|
|
16
|
+
"@graphcommerce/prettier-config-pwa": "6.2.0-canary.45",
|
|
17
|
+
"@graphcommerce/typescript-config-pwa": "6.2.0-canary.45"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@graphcommerce/ecommerce-ui": "6.2.0-canary.
|
|
21
|
-
"@graphcommerce/framer-utils": "6.2.0-canary.
|
|
22
|
-
"@graphcommerce/graphql": "6.2.0-canary.
|
|
23
|
-
"@graphcommerce/graphql-mesh": "6.2.0-canary.
|
|
24
|
-
"@graphcommerce/image": "6.2.0-canary.
|
|
25
|
-
"@graphcommerce/magento-graphql": "6.2.0-canary.
|
|
26
|
-
"@graphcommerce/magento-store": "6.2.0-canary.
|
|
27
|
-
"@graphcommerce/next-ui": "6.2.0-canary.
|
|
28
|
-
"@graphcommerce/react-hook-form": "6.2.0-canary.
|
|
20
|
+
"@graphcommerce/ecommerce-ui": "6.2.0-canary.45",
|
|
21
|
+
"@graphcommerce/framer-utils": "6.2.0-canary.45",
|
|
22
|
+
"@graphcommerce/graphql": "6.2.0-canary.45",
|
|
23
|
+
"@graphcommerce/graphql-mesh": "6.2.0-canary.45",
|
|
24
|
+
"@graphcommerce/image": "6.2.0-canary.45",
|
|
25
|
+
"@graphcommerce/magento-graphql": "6.2.0-canary.45",
|
|
26
|
+
"@graphcommerce/magento-store": "6.2.0-canary.45",
|
|
27
|
+
"@graphcommerce/next-ui": "6.2.0-canary.45",
|
|
28
|
+
"@graphcommerce/react-hook-form": "6.2.0-canary.45"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@lingui/react": "^4.2.1",
|