@graphcommerce/magento-customer 3.0.1
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 +294 -0
- package/components/AccountAddress/AccountAddress.gql.ts +4 -0
- package/components/AccountAddress/AccountAddress.graphql +6 -0
- package/components/AccountAddress/index.tsx +59 -0
- package/components/AccountAddresses/AccountAddresses.gql.ts +4 -0
- package/components/AccountAddresses/AccountAddresses.graphql +5 -0
- package/components/AccountAddresses/UpdateDefaultAddress.gql.ts +14 -0
- package/components/AccountAddresses/UpdateDefaultAddress.graphql +14 -0
- package/components/AccountAddresses/index.tsx +101 -0
- package/components/AddressFields/index.tsx +189 -0
- package/components/AddressMultiLine/index.tsx +68 -0
- package/components/AddressSingleLine/index.tsx +34 -0
- package/components/ApolloCustomerError/ApolloCustomerErrorAlert.tsx +21 -0
- package/components/ApolloCustomerError/ApolloCustomerErrorFullPage.tsx +46 -0
- package/components/ChangeNameForm/UpdateCustomerName.gql.ts +14 -0
- package/components/ChangeNameForm/UpdateCustomerName.graphql +9 -0
- package/components/ChangeNameForm/index.tsx +66 -0
- package/components/ChangePasswordForm/ChangePassword.gql.ts +13 -0
- package/components/ChangePasswordForm/ChangePassword.graphql +5 -0
- package/components/ChangePasswordForm/ChangePasswordForm.tsx +93 -0
- package/components/CreateCustomerAddressForm/CreateCustomerAddress.gql.ts +28 -0
- package/components/CreateCustomerAddressForm/CreateCustomerAddress.graphql +41 -0
- package/components/CreateCustomerAddressForm/CustomerAddress.gql.ts +4 -0
- package/components/CreateCustomerAddressForm/CustomerAddress.graphql +22 -0
- package/components/CreateCustomerAddressForm/CustomerAddressEdit.gql.ts +4 -0
- package/components/CreateCustomerAddressForm/CustomerAddressEdit.graphql +4 -0
- package/components/CreateCustomerAddressForm/index.tsx +98 -0
- package/components/CustomerFab/index.tsx +55 -0
- package/components/CustomerMenuFabItem/index.tsx +68 -0
- package/components/DeleteCustomerAddressForm/DeleteCustomerAddressForm.gql.ts +12 -0
- package/components/DeleteCustomerAddressForm/DeleteCustomerAddressForm.graphql +3 -0
- package/components/DeleteCustomerAddressForm/index.tsx +46 -0
- package/components/EditAddressForm/UpdateCustomerAddress.gql.ts +29 -0
- package/components/EditAddressForm/UpdateCustomerAddress.graphql +43 -0
- package/components/EditAddressForm/index.tsx +127 -0
- package/components/ForgotPasswordForm/ForgotPassword.gql.ts +12 -0
- package/components/ForgotPasswordForm/ForgotPassword.graphql +3 -0
- package/components/ForgotPasswordForm/ForgotPasswordForm.tsx +73 -0
- package/components/InlineAccount/InlineAccount.gql.ts +12 -0
- package/components/InlineAccount/InlineAccount.graphql +10 -0
- package/components/InlineAccount/index.tsx +149 -0
- package/components/NameFields/index.tsx +89 -0
- package/components/ResetPasswordForm/ResetPassword.gql.ts +14 -0
- package/components/ResetPasswordForm/ResetPassword.graphql +3 -0
- package/components/ResetPasswordForm/index.tsx +98 -0
- package/components/SignInForm/SignIn.gql.ts +13 -0
- package/components/SignInForm/SignIn.graphql +5 -0
- package/components/SignInForm/SignInForm.tsx +96 -0
- package/components/SignInForm/SignInFormInline.tsx +68 -0
- package/components/SignOutForm/SignOutForm.gql.ts +10 -0
- package/components/SignOutForm/SignOutForm.graphql +5 -0
- package/components/SignOutForm/index.tsx +30 -0
- package/components/SignUpForm/SignUp.gql.ts +22 -0
- package/components/SignUpForm/SignUp.graphql +36 -0
- package/components/SignUpForm/SignUpForm.tsx +90 -0
- package/components/SignUpForm/SignUpFormInline.tsx +113 -0
- package/components/UpdateCustomerEmailForm/UpdateCustomerEmail.gql.ts +13 -0
- package/components/UpdateCustomerEmailForm/UpdateCustomerEmail.graphql +7 -0
- package/components/UpdateCustomerEmailForm/index.tsx +133 -0
- package/components/UpdateDefaultAddressForm/index.tsx +91 -0
- package/components/index.ts +29 -0
- package/hooks/Customer.gql.ts +10 -0
- package/hooks/Customer.graphql +5 -0
- package/hooks/CustomerInfo.gql.ts +4 -0
- package/hooks/CustomerInfo.graphql +20 -0
- package/hooks/CustomerToken.gql.ts +10 -0
- package/hooks/CustomerToken.graphql +8 -0
- package/hooks/CustomerToken.graphqls +8 -0
- package/hooks/IsEmailAvailable.gql.ts +12 -0
- package/hooks/IsEmailAvailable.graphql +5 -0
- package/hooks/index.ts +7 -0
- package/hooks/useExtractCustomerErrors.tsx +42 -0
- package/hooks/useFormIsEmailAvailable.tsx +69 -0
- package/index.ts +4 -0
- package/next-env.d.ts +4 -0
- package/package.json +41 -0
- package/tsconfig.json +5 -0
- package/typePolicies.ts +91 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
|
5
|
+
|
|
6
|
+
export const CustomerTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CustomerToken"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customerToken"},"directives":[{"kind":"Directive","name":{"kind":"Name","value":"client"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"valid"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode<CustomerTokenQuery, CustomerTokenQueryVariables>;
|
|
7
|
+
export type CustomerTokenQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export type CustomerTokenQuery = { customerToken?: Types.Maybe<{ __typename: 'CustomerToken', token?: Types.Maybe<string>, valid?: Types.Maybe<boolean>, createdAt?: Types.Maybe<string> }> };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import * as Types from '@graphcommerce/graphql';
|
|
3
|
+
|
|
4
|
+
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
|
|
5
|
+
|
|
6
|
+
export const IsEmailAvailableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"IsEmailAvailable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"isEmailAvailable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"is_email_available"}}]}}]}}]} as unknown as DocumentNode<IsEmailAvailableQuery, IsEmailAvailableQueryVariables>;
|
|
7
|
+
export type IsEmailAvailableQueryVariables = Types.Exact<{
|
|
8
|
+
email: Types.Scalars['String'];
|
|
9
|
+
}>;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export type IsEmailAvailableQuery = { isEmailAvailable?: Types.Maybe<{ is_email_available?: Types.Maybe<boolean> }> };
|
package/hooks/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as useFormIsEmailAvailable } from './useFormIsEmailAvailable'
|
|
2
|
+
export * from './useExtractCustomerErrors'
|
|
3
|
+
|
|
4
|
+
export * from './Customer.gql'
|
|
5
|
+
export * from './CustomerToken.gql'
|
|
6
|
+
export * from './CustomerInfo.gql'
|
|
7
|
+
export * from './IsEmailAvailable.gql'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ApolloError, useApolloClient } from '@apollo/client'
|
|
2
|
+
import { graphqlErrorByCategory } from '@graphcommerce/magento-graphql'
|
|
3
|
+
import { useEffect } from 'react'
|
|
4
|
+
import { CustomerTokenDocument } from './CustomerToken.gql'
|
|
5
|
+
|
|
6
|
+
export type UseExtractErrors = { error?: ApolloError }
|
|
7
|
+
export function useExtractCustomerErrors({ error }: UseExtractErrors) {
|
|
8
|
+
const client = useApolloClient()
|
|
9
|
+
|
|
10
|
+
const [newError, unauthorized] = graphqlErrorByCategory({
|
|
11
|
+
category: 'graphql-authorization',
|
|
12
|
+
error,
|
|
13
|
+
extract: false,
|
|
14
|
+
mask: 'You need to login to continue',
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!unauthorized) return
|
|
19
|
+
|
|
20
|
+
const { customerToken } =
|
|
21
|
+
client.cache.readQuery({
|
|
22
|
+
query: CustomerTokenDocument,
|
|
23
|
+
}) ?? {}
|
|
24
|
+
|
|
25
|
+
if (customerToken) {
|
|
26
|
+
// Write arbitrary old token to document
|
|
27
|
+
client.cache.writeQuery({
|
|
28
|
+
query: CustomerTokenDocument,
|
|
29
|
+
data: {
|
|
30
|
+
customerToken: {
|
|
31
|
+
...customerToken,
|
|
32
|
+
createdAt: new Date('2000').toUTCString(),
|
|
33
|
+
valid: false,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
broadcast: true,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
}, [client.cache, unauthorized])
|
|
40
|
+
|
|
41
|
+
return { error: newError, unauthorized }
|
|
42
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useQuery } from '@apollo/client'
|
|
2
|
+
import { useFormAutoSubmit, useFormGqlQuery, useFormPersist } from '@graphcommerce/react-hook-form'
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
import { CustomerDocument } from './Customer.gql'
|
|
5
|
+
import { CustomerTokenDocument } from './CustomerToken.gql'
|
|
6
|
+
import { IsEmailAvailableDocument } from './IsEmailAvailable.gql'
|
|
7
|
+
|
|
8
|
+
type useFormIsEmailAvailableProps = {
|
|
9
|
+
email?: string | null
|
|
10
|
+
onSubmitted?: (data: { email: string }) => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function useFormIsEmailAvailable(props: useFormIsEmailAvailableProps) {
|
|
14
|
+
const { email, onSubmitted } = props
|
|
15
|
+
const { data: token } = useQuery(CustomerTokenDocument)
|
|
16
|
+
const customerQuery = useQuery(CustomerDocument, {
|
|
17
|
+
ssr: false,
|
|
18
|
+
skip: typeof token === 'undefined',
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const form = useFormGqlQuery(IsEmailAvailableDocument, {
|
|
22
|
+
mode: 'onChange',
|
|
23
|
+
defaultValues: { email: email ?? '' },
|
|
24
|
+
})
|
|
25
|
+
const { formState, data, handleSubmit } = form
|
|
26
|
+
|
|
27
|
+
const submit = handleSubmit(onSubmitted || (() => {}))
|
|
28
|
+
const autoSubmitting = useFormAutoSubmit({ form, submit })
|
|
29
|
+
|
|
30
|
+
const hasAccount = data?.isEmailAvailable?.is_email_available === false
|
|
31
|
+
const { isDirty, isSubmitSuccessful, isSubmitted, isSubmitting, isValid } = formState
|
|
32
|
+
|
|
33
|
+
const isLoggedIn = token?.customerToken && token?.customerToken.valid
|
|
34
|
+
|
|
35
|
+
const [mode, setMode] = useState<'email' | 'signin' | 'signup' | 'signedin' | 'session-expired'>(
|
|
36
|
+
token?.customerToken && token?.customerToken.valid ? 'signedin' : 'email',
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
useFormPersist({ form, name: 'IsEmailAvailable' })
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (isLoggedIn) {
|
|
43
|
+
setMode('signedin')
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
if (isSubmitting) return
|
|
47
|
+
if (!isValid) {
|
|
48
|
+
setMode('email')
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
if (!isDirty && isSubmitted && isSubmitSuccessful && isValid)
|
|
52
|
+
setMode(hasAccount ? 'signin' : 'signup')
|
|
53
|
+
|
|
54
|
+
if (customerQuery.data?.customer && token && token.customerToken && !token.customerToken.valid)
|
|
55
|
+
setMode(isSubmitSuccessful ? 'signin' : 'session-expired')
|
|
56
|
+
}, [
|
|
57
|
+
customerQuery.data?.customer,
|
|
58
|
+
hasAccount,
|
|
59
|
+
isDirty,
|
|
60
|
+
isLoggedIn,
|
|
61
|
+
isSubmitSuccessful,
|
|
62
|
+
isSubmitted,
|
|
63
|
+
isSubmitting,
|
|
64
|
+
isValid,
|
|
65
|
+
token,
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
return { mode, form, token, submit, autoSubmitting, hasAccount }
|
|
69
|
+
}
|
package/index.ts
ADDED
package/next-env.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphcommerce/magento-customer",
|
|
3
|
+
"version": "3.0.1",
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"prettier": "@graphcommerce/prettier-config-pwa",
|
|
6
|
+
"browserslist": [
|
|
7
|
+
"extends @graphcommerce/browserslist-config-pwa"
|
|
8
|
+
],
|
|
9
|
+
"eslintConfig": {
|
|
10
|
+
"extends": "@graphcommerce/eslint-config-pwa",
|
|
11
|
+
"parserOptions": {
|
|
12
|
+
"project": "./tsconfig.json"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@graphcommerce/browserslist-config-pwa": "^3.0.1",
|
|
17
|
+
"@graphcommerce/eslint-config-pwa": "^3.0.1",
|
|
18
|
+
"@graphcommerce/prettier-config-pwa": "^3.0.1",
|
|
19
|
+
"@graphcommerce/typescript-config-pwa": "^3.0.1",
|
|
20
|
+
"@playwright/test": "^1.14.1"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@apollo/client": "^3.3.21",
|
|
24
|
+
"@graphcommerce/graphql": "^2.103.1",
|
|
25
|
+
"@graphcommerce/image": "^2.104.1",
|
|
26
|
+
"@graphcommerce/magento-cart": "^3.0.1",
|
|
27
|
+
"@graphcommerce/magento-graphql": "^2.103.1",
|
|
28
|
+
"@graphcommerce/magento-product": "^3.0.1",
|
|
29
|
+
"@graphcommerce/magento-store": "^3.0.1",
|
|
30
|
+
"@graphcommerce/next-ui": "^3.0.1",
|
|
31
|
+
"@graphcommerce/react-hook-form": "^2.102.1",
|
|
32
|
+
"@graphql-typed-document-node/core": "^3.1.0",
|
|
33
|
+
"@material-ui/core": "^4.12.3",
|
|
34
|
+
"@material-ui/lab": "^4.0.0-alpha.60",
|
|
35
|
+
"clsx": "^1.1.1",
|
|
36
|
+
"framer-motion": "^4.1.17",
|
|
37
|
+
"next": "^11.1.2",
|
|
38
|
+
"react": "^17.0.2",
|
|
39
|
+
"react-dom": "^17.0.2"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/tsconfig.json
ADDED
package/typePolicies.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { FieldPolicy, FieldReadFunction } from '@apollo/client'
|
|
2
|
+
import {
|
|
3
|
+
CustomerToken,
|
|
4
|
+
MigrateCache,
|
|
5
|
+
Mutation,
|
|
6
|
+
Query,
|
|
7
|
+
TypedTypePolicies,
|
|
8
|
+
} from '@graphcommerce/graphql'
|
|
9
|
+
import { CustomerTokenDocument } from './hooks/CustomerToken.gql'
|
|
10
|
+
import { IsEmailAvailableDocument } from './hooks/IsEmailAvailable.gql'
|
|
11
|
+
|
|
12
|
+
const revokeCustomerToken: FieldPolicy<Mutation['revokeCustomerToken']> = {
|
|
13
|
+
merge(_existing, incoming, options) {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
15
|
+
options.cache.reset()
|
|
16
|
+
return incoming
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const TOKEN_EXPIRATION_MS = 60 * 60 * 1000
|
|
21
|
+
|
|
22
|
+
const valid: FieldPolicy<CustomerToken['valid']> = {
|
|
23
|
+
read(existing, options) {
|
|
24
|
+
if (existing === undefined) return existing
|
|
25
|
+
|
|
26
|
+
const ref = options.toReference({ __ref: 'CustomerToken' })
|
|
27
|
+
const createdAt = options.readField<string>('createdAt', ref)
|
|
28
|
+
|
|
29
|
+
if (!createdAt) return existing
|
|
30
|
+
|
|
31
|
+
return new Date().getTime() - new Date(createdAt).getTime() < TOKEN_EXPIRATION_MS
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const generateCustomerToken: FieldPolicy<Mutation['generateCustomerToken']> = {
|
|
36
|
+
keyArgs: () => '',
|
|
37
|
+
merge(_existing, incoming, options) {
|
|
38
|
+
if (!options.isReference(incoming)) return incoming
|
|
39
|
+
|
|
40
|
+
const write = () => {
|
|
41
|
+
options.cache.writeQuery({
|
|
42
|
+
query: CustomerTokenDocument,
|
|
43
|
+
broadcast: true,
|
|
44
|
+
data: {
|
|
45
|
+
customerToken: {
|
|
46
|
+
__typename: 'CustomerToken',
|
|
47
|
+
token: options.readField('token', incoming) as string,
|
|
48
|
+
createdAt: new Date().toUTCString(),
|
|
49
|
+
valid: true,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
write()
|
|
55
|
+
|
|
56
|
+
// Broadcasts the query after the token expiration so UI gets updated
|
|
57
|
+
setTimeout(write, TOKEN_EXPIRATION_MS)
|
|
58
|
+
return incoming
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const createCustomer: FieldPolicy<Mutation['createCustomer']> = {
|
|
63
|
+
merge(_existing, incoming, options) {
|
|
64
|
+
if (incoming?.customer.email) {
|
|
65
|
+
options.cache.writeQuery({
|
|
66
|
+
query: IsEmailAvailableDocument,
|
|
67
|
+
variables: { email: incoming?.customer.email },
|
|
68
|
+
data: { isEmailAvailable: { is_email_available: false } },
|
|
69
|
+
broadcast: true,
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return incoming
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// const customer: FieldReadFunction<Query['customer']> = (incoming, options) => {
|
|
78
|
+
// if (!options.canRead(incoming)) return null
|
|
79
|
+
// return incoming
|
|
80
|
+
// }
|
|
81
|
+
|
|
82
|
+
export const customerTypePolicies: TypedTypePolicies = {
|
|
83
|
+
// Query: { fields: { customer } },
|
|
84
|
+
Mutation: { fields: { generateCustomerToken, revokeCustomerToken, createCustomer } },
|
|
85
|
+
CustomerToken: { fields: { valid } },
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const migrateCustomer: MigrateCache = (oldCache, newCache) => {
|
|
89
|
+
const token = oldCache.readQuery({ query: CustomerTokenDocument })
|
|
90
|
+
newCache.writeQuery({ query: CustomerTokenDocument, data: token, broadcast: true })
|
|
91
|
+
}
|