From 68adb9026ba9ae2fd0c74607b92bc9b840f34d21 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Tue, 28 Sep 2021 16:04:47 +0700 Subject: [PATCH 01/16] :sparkles: feat: verify customer :%s --- next-env.d.ts | 3 -- pages/verify.tsx | 34 +++++++++++++ .../components/FormRegister/FormRegister.tsx | 48 +++++++++++++++---- src/components/hooks/index.ts | 2 + src/components/hooks/useSignup.tsx | 4 +- src/components/hooks/useVerifyCustomer.tsx | 43 +++++++++++++++++ src/graphql/mutation/index.ts | 1 + src/graphql/mutation/user.mutation.ts | 16 +++++++ src/graphql/query/index.ts | 1 + src/graphql/query/user.query.ts | 1 + 10 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 pages/verify.tsx create mode 100644 src/components/hooks/useVerifyCustomer.tsx create mode 100644 src/graphql/mutation/index.ts create mode 100644 src/graphql/mutation/user.mutation.ts create mode 100644 src/graphql/query/index.ts create mode 100644 src/graphql/query/user.query.ts diff --git a/next-env.d.ts b/next-env.d.ts index 9bc3dd46b..c6643fda1 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,3 @@ /// /// /// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/pages/verify.tsx b/pages/verify.tsx new file mode 100644 index 000000000..f16702e18 --- /dev/null +++ b/pages/verify.tsx @@ -0,0 +1,34 @@ +import { useRouter } from 'next/router' +import { useEffect } from 'react' +import { Layout } from 'src/components/common' +import LoadingCommon from 'src/components/common/LoadingCommon/LoadingCommon' +import { useVerifyCustomer } from 'src/components/hooks' + +export default function VerifyCustomer() { + const router = useRouter() + const { error, loading, verify } = useVerifyCustomer() + + useEffect(() => { + const { token } = router.query + console.log("token: ", token) + if (token) { + verify({ token: token.toString() }) + } + }, []) + + return ( +
+ { + loading ? <> + + Verifing your account .... + :
+ {error?.message} +
+ } +
+ ) +} + + +VerifyCustomer.Layout = Layout diff --git a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx index 66ec1f8a4..afcfd259c 100644 --- a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx +++ b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx @@ -1,10 +1,11 @@ -import React, { useEffect, useRef } from 'react' -import { ButtonCommon, Inputcommon, InputPassword } from 'src/components/common' -import s from '../FormAuthen.module.scss' -import styles from './FormRegister.module.scss' -import SocialAuthen from '../SocialAuthen/SocialAuthen' import classNames from 'classnames' +import React, { useEffect, useRef, useState } from 'react' +import { ButtonCommon, Inputcommon, InputPassword } from 'src/components/common' import { CustomInputCommon } from 'src/utils/type.utils' +import { useSignup } from '../../../../hooks' +import s from '../FormAuthen.module.scss' +import SocialAuthen from '../SocialAuthen/SocialAuthen' +import styles from './FormRegister.module.scss' interface Props { isHide: boolean, @@ -13,6 +14,10 @@ interface Props { const FormRegister = ({ onSwitch, isHide }: Props) => { const emailRef = useRef(null) + const { loading, signup, error } = useSignup() + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + useEffect(() => { if (!isHide) { @@ -20,6 +25,18 @@ const FormRegister = ({ onSwitch, isHide }: Props) => { } }, [isHide]) + const onSignup = () => { + // TODO: validate fields + signup({ email, password }) + } + + + useEffect(() => { + if (error) { + alert(error.message) + } + }, [error]) + return (
{ })}>
- - + setEmail(val.toString())} + /> + setPassword(val.toString())} + /> +
Must contain 8 characters with at least 1 uppercase and 1 lowercase letter and either 1 number or 1 special character.
- Create Account + + Create Account +
diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index cf83feb42..4a25fd98e 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -1 +1,3 @@ export { default as useModalCommon } from './useModalCommon' +export { default as useVerifyCustomer } from './useVerifyCustomer' +export { default as useSignup } from './useSignup' diff --git a/src/components/hooks/useSignup.tsx b/src/components/hooks/useSignup.tsx index b06781e88..91a5fe186 100644 --- a/src/components/hooks/useSignup.tsx +++ b/src/components/hooks/useSignup.tsx @@ -22,8 +22,8 @@ const query = gql` interface SignupInput { email: string - firstName: string - lastName: string + firstName?: string + lastName?: string password: string } diff --git a/src/components/hooks/useVerifyCustomer.tsx b/src/components/hooks/useVerifyCustomer.tsx new file mode 100644 index 000000000..edd789d13 --- /dev/null +++ b/src/components/hooks/useVerifyCustomer.tsx @@ -0,0 +1,43 @@ +import { LoginMutation } from '@framework/schema' +import { useState } from 'react' +import { CommonError } from 'src/domains/interfaces/CommonError' +import rawFetcher from 'src/utils/rawFetcher' +import { VERIFY_CUSTOMER_ACCOUNT } from '../../graphql/mutation' +import useActiveCustomer from './useActiveCustomer' + +interface VerifyInput { + token: string + password?: string +} + +const useVerifyCustomer = () => { + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + // const { mutate } = useActiveCustomer() + + const verify = (options: VerifyInput) => { + setError(null) + setLoading(true) + rawFetcher({ + query: VERIFY_CUSTOMER_ACCOUNT, + variables: options, + }) + .then(({ data, headers }) => { + console.log("data: ", data) + // if (data.login.__typename !== 'CurrentUser') { + // throw CommonError.create(data.login.message, data.login.errorCode) + // } + // const authToken = headers.get('vendure-auth-token') + // if (authToken != null) { + // localStorage.setItem('token', authToken) + // return mutate() + // } + }) + .catch(setError) + .finally(() => setLoading(false)) + } + + return { loading, verify, error } +} + +export default useVerifyCustomer diff --git a/src/graphql/mutation/index.ts b/src/graphql/mutation/index.ts new file mode 100644 index 000000000..7ea0d4f82 --- /dev/null +++ b/src/graphql/mutation/index.ts @@ -0,0 +1 @@ +export * from './user.mutation' diff --git a/src/graphql/mutation/user.mutation.ts b/src/graphql/mutation/user.mutation.ts new file mode 100644 index 000000000..a48074ef0 --- /dev/null +++ b/src/graphql/mutation/user.mutation.ts @@ -0,0 +1,16 @@ +import { gql } from 'graphql-request' + + +export const VERIFY_CUSTOMER_ACCOUNT = gql` +mutation verifyCustomerAccount(token: String!, password: String) { + verifyCustomerAccount( token: $token, password: $password) { + ...on CurrentUser { + id + identifier + } + ...ErrorResult + } +} +` + + diff --git a/src/graphql/query/index.ts b/src/graphql/query/index.ts new file mode 100644 index 000000000..47c53bc6d --- /dev/null +++ b/src/graphql/query/index.ts @@ -0,0 +1 @@ +// export * from './user.mutation' diff --git a/src/graphql/query/user.query.ts b/src/graphql/query/user.query.ts new file mode 100644 index 000000000..e9e7409b4 --- /dev/null +++ b/src/graphql/query/user.query.ts @@ -0,0 +1 @@ +// query here \ No newline at end of file From cdad66b56b685e1712290fb8bb8773b088fee538 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Tue, 28 Sep 2021 17:50:55 +0700 Subject: [PATCH 02/16] :sparkles: feat: VerifyCustomerAccountMutation :%s --- framework/vendure/schema.d.ts | 30 +++++++++++++ next-env.d.ts | 3 ++ pages/verify.tsx | 30 +------------ .../common/LoadingCommon/LoadingCommon.tsx | 24 ++++++----- src/components/hooks/useProduct.tsx | 1 + src/components/hooks/useVerifyCustomer.tsx | 42 ++++++++++++------- .../modules/verify-customer/index.tsx | 2 + src/graphql/mutation/user.mutation.ts | 10 +++-- src/utils/fetcher.ts | 8 ++-- 9 files changed, 89 insertions(+), 61 deletions(-) create mode 100644 src/components/hooks/useProduct.tsx create mode 100644 src/components/modules/verify-customer/index.tsx diff --git a/framework/vendure/schema.d.ts b/framework/vendure/schema.d.ts index 9d6b53c52..b0b0170d7 100644 --- a/framework/vendure/schema.d.ts +++ b/framework/vendure/schema.d.ts @@ -3095,6 +3095,36 @@ export type LoginMutation = { __typename?: 'Mutation' } & { >) } +export type VerifyCustomerAccountVariables = Exact<{ + token: Scalars['String'] + password?: Maybe +}> + +export type VerifyCustomerAccountMutation = { __typename?: 'Mutation' } & { + verifyCustomerAccount: + | ({ __typename: 'CurrentUser' } & Pick) + | ({ __typename: 'VerificationTokenInvalidError' } & Pick< + VerificationTokenInvalidError, + 'errorCode' | 'message' + >) + | ({ __typename: 'VerificationTokenExpiredError' } & Pick< + VerificationTokenExpiredError, + 'errorCode' | 'message' + >) + | ({ __typename: 'MissingPasswordError' } & Pick< + MissingPasswordError, + 'errorCode' | 'message' + >) + | ({ __typename: 'PasswordAlreadySetError' } & Pick< + PasswordAlreadySetError, + 'errorCode' | 'message' + >) + | ({ __typename: 'NativeAuthStrategyError' } & Pick< + NativeAuthStrategyError, + 'errorCode' | 'message' + >) +} + export type LogoutMutationVariables = Exact<{ [key: string]: never }> export type LogoutMutation = { __typename?: 'Mutation' } & { diff --git a/next-env.d.ts b/next-env.d.ts index c6643fda1..9bc3dd46b 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,3 +1,6 @@ /// /// /// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/pages/verify.tsx b/pages/verify.tsx index f16702e18..97d9743b1 100644 --- a/pages/verify.tsx +++ b/pages/verify.tsx @@ -1,34 +1,8 @@ -import { useRouter } from 'next/router' -import { useEffect } from 'react' import { Layout } from 'src/components/common' -import LoadingCommon from 'src/components/common/LoadingCommon/LoadingCommon' -import { useVerifyCustomer } from 'src/components/hooks' +import { VerifyCustomerAccount } from 'src/components/modules/verify-customer' export default function VerifyCustomer() { - const router = useRouter() - const { error, loading, verify } = useVerifyCustomer() - - useEffect(() => { - const { token } = router.query - console.log("token: ", token) - if (token) { - verify({ token: token.toString() }) - } - }, []) - - return ( -
- { - loading ? <> - - Verifing your account .... - :
- {error?.message} -
- } -
- ) + return } - VerifyCustomer.Layout = Layout diff --git a/src/components/common/LoadingCommon/LoadingCommon.tsx b/src/components/common/LoadingCommon/LoadingCommon.tsx index 1e8ffe340..f75bc370b 100644 --- a/src/components/common/LoadingCommon/LoadingCommon.tsx +++ b/src/components/common/LoadingCommon/LoadingCommon.tsx @@ -1,15 +1,17 @@ -import React from "react"; +import React from 'react' import s from './LoadingCommon.module.scss' -const LoadingCommon = () => { - - return ( -
-
-
-

Loading...

-
- ) +interface Props { + description?: string } -export default LoadingCommon \ No newline at end of file +const LoadingCommon = ({ description = 'Loading...' }: Props) => { + return ( +
+
+

{description}

+
+ ) +} + +export default LoadingCommon diff --git a/src/components/hooks/useProduct.tsx b/src/components/hooks/useProduct.tsx new file mode 100644 index 000000000..ab2eb4eb0 --- /dev/null +++ b/src/components/hooks/useProduct.tsx @@ -0,0 +1 @@ +// here diff --git a/src/components/hooks/useVerifyCustomer.tsx b/src/components/hooks/useVerifyCustomer.tsx index edd789d13..a4af3184e 100644 --- a/src/components/hooks/useVerifyCustomer.tsx +++ b/src/components/hooks/useVerifyCustomer.tsx @@ -1,4 +1,4 @@ -import { LoginMutation } from '@framework/schema' +import { VerifyCustomerAccountMutation } from '@framework/schema' import { useState } from 'react' import { CommonError } from 'src/domains/interfaces/CommonError' import rawFetcher from 'src/utils/rawFetcher' @@ -13,28 +13,40 @@ interface VerifyInput { const useVerifyCustomer = () => { const [loading, setLoading] = useState(false) const [error, setError] = useState(null) - // const { mutate } = useActiveCustomer() + const { mutate } = useActiveCustomer() - const verify = (options: VerifyInput) => { + const verify = ( + options: VerifyInput, + fCallBack?: (isSuccess: boolean) => void + ) => { setError(null) setLoading(true) - rawFetcher({ + rawFetcher({ query: VERIFY_CUSTOMER_ACCOUNT, variables: options, }) .then(({ data, headers }) => { - console.log("data: ", data) - // if (data.login.__typename !== 'CurrentUser') { - // throw CommonError.create(data.login.message, data.login.errorCode) - // } - // const authToken = headers.get('vendure-auth-token') - // if (authToken != null) { - // localStorage.setItem('token', authToken) - // return mutate() - // } + if (data.verifyCustomerAccount.__typename !== 'CurrentUser') { + throw CommonError.create( + data.verifyCustomerAccount.message, + data.verifyCustomerAccount.errorCode + ) + } + fCallBack && fCallBack(true) + + const authToken = headers.get('vendure-auth-token') + if (authToken != null) { + localStorage.setItem('token', authToken) + return mutate() + } + }) + .catch((err) => { + setError(err) + fCallBack && fCallBack(false) + }) + .finally(() => { + setLoading(false) }) - .catch(setError) - .finally(() => setLoading(false)) } return { loading, verify, error } diff --git a/src/components/modules/verify-customer/index.tsx b/src/components/modules/verify-customer/index.tsx new file mode 100644 index 000000000..8b8fe4dbe --- /dev/null +++ b/src/components/modules/verify-customer/index.tsx @@ -0,0 +1,2 @@ +export { default as VerifyCustomerAccount } from './VerifyCustomerAccount/VerifyCustomerAccount' + diff --git a/src/graphql/mutation/user.mutation.ts b/src/graphql/mutation/user.mutation.ts index a48074ef0..40812db19 100644 --- a/src/graphql/mutation/user.mutation.ts +++ b/src/graphql/mutation/user.mutation.ts @@ -2,15 +2,19 @@ import { gql } from 'graphql-request' export const VERIFY_CUSTOMER_ACCOUNT = gql` -mutation verifyCustomerAccount(token: String!, password: String) { +mutation verifyCustomerAccount($token: String!, $password: String) { verifyCustomerAccount( token: $token, password: $password) { + __typename ...on CurrentUser { id identifier } - ...ErrorResult + ... on ErrorResult { + errorCode + message + } + } } -} ` diff --git a/src/utils/fetcher.ts b/src/utils/fetcher.ts index e92696970..53b736e5e 100644 --- a/src/utils/fetcher.ts +++ b/src/utils/fetcher.ts @@ -10,11 +10,11 @@ interface QueryOptions { const fetcher = async (options: QueryOptions): Promise => { const { query, variables } = options - console.log('query') - console.log(options) + // console.log('query') + // console.log(options) const token = localStorage.getItem('token') - console.log('token') - console.log(token) + // console.log('token') + // console.log(token) const res = await request( process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL as string, query, From 75bea33a0f9ee621585b21daf0214a112bf0b2a1 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Tue, 28 Sep 2021 17:51:07 +0700 Subject: [PATCH 03/16] :art: styles: page verify customer :%s --- .../VerifyCustomerAccount.module.scss | 16 ++++++ .../VerifyCustomerAccount.tsx | 50 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.module.scss create mode 100644 src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx diff --git a/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.module.scss b/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.module.scss new file mode 100644 index 000000000..c0abfceda --- /dev/null +++ b/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.module.scss @@ -0,0 +1,16 @@ +@import "../../../../styles/utilities"; + +.verifyCustomerAccount { + @apply spacing-horizontal; + min-height: 25rem; + margin: 5rem auto 10rem; + display: flex; + justify-content: center; + + .result { + @apply flex flex-col justify-center items-center; + .message { + margin-bottom: 1.6rem; + } + } +} diff --git a/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx b/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx new file mode 100644 index 000000000..75caecb5b --- /dev/null +++ b/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx @@ -0,0 +1,50 @@ +import { useRouter } from 'next/router' +import { useEffect, useState } from 'react' +import { ButtonCommon } from 'src/components/common' +import LoadingCommon from 'src/components/common/LoadingCommon/LoadingCommon' +import { useVerifyCustomer } from 'src/components/hooks' +import { ROUTE } from 'src/utils/constanst.utils' +import s from './VerifyCustomerAccount.module.scss' +import Link from 'next/link' + +export default function VerifyCustomerAccount() { + const router = useRouter() + const [isVerified, setIsVerified] = useState(false) + const { error, loading, verify } = useVerifyCustomer() + + useEffect(() => { + const token = router.query.token + if (token && !isVerified) { + setIsVerified(true) + verify({ token: token.toString() }) + } + }, [router, verify, isVerified]) + + return ( +
+ {loading || !isVerified ? ( +
+ +
+ ) : error ? ( +
+
Error: {error?.message}
+ + + Back to home + + +
+ ) : ( +
+
Congratulation! Verified account successfully
+ + + Back to home + + +
+ )} +
+ ) +} From c0e703e1ae50892079f587f9783129aab0a8d66c Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Tue, 28 Sep 2021 18:08:17 +0700 Subject: [PATCH 04/16] :recycle: enhan: disable btn when loading :%s --- pages/test.tsx | 90 +++---------------- .../ButtonCommon/ButtonCommon.module.scss | 60 +++++++------ .../common/ButtonCommon/ButtonCommon.tsx | 2 +- .../components/FormRegister/FormRegister.tsx | 2 + 4 files changed, 49 insertions(+), 105 deletions(-) diff --git a/pages/test.tsx b/pages/test.tsx index 9d0cb6129..b40ad354a 100644 --- a/pages/test.tsx +++ b/pages/test.tsx @@ -1,85 +1,19 @@ +import { useEffect, useState } from 'react' import { - FeaturedProductCard, - Layout -} from 'src/components/common'; -import { HomeBanner } from 'src/components/modules/home'; -// import { RecipeListPage } from 'src/components/modules/recipes'; -import { OPTION_ALL, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils'; -import { PRODUCT_DATA_TEST, PRODUCT_DATA_TEST_PAGE } from 'src/utils/demo-data'; - -const CATEGORY = [ - { - name: 'All', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=${OPTION_ALL}`, - }, - { - name: 'Veggie', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=veggie`, - }, - { - name: 'Seafood', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=seafood`, - }, - { - name: 'Frozen', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=frozen`, - }, - { - name: 'Coffee Bean', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=coffee-bean`, - }, - { - name: 'Sauce', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=sauce`, - }, -] -const BRAND = [ - { - name: 'Maggi', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=veggie`, - }, - { - name: 'Cholimes', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=seafood`, - }, - { - name: 'Chinsu', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=frozen`, - }] - -const FEATURED = [ - - { - name: 'Best Sellers', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=veggie`, - }, - { - name: 'Sales', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=seafood`, - }, - { - name: 'New Item', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=frozen`, - }, - { - name: 'Viewed', - link: `${ROUTE.PRODUCTS}/?${QUERY_KEY.BRAND}=viewed`, - } -]; - - - -const data = PRODUCT_DATA_TEST[0] + ButtonCommon, Layout +} from 'src/components/common' export default function Test() { + const [isLoading, setisLoading] = useState(false) + useEffect(() => { + setTimeout(() => { + setisLoading(true) + }, 3000) + }, []) return ( <> - - + Back to home + Back to home + Back to home ) } diff --git a/src/components/common/ButtonCommon/ButtonCommon.module.scss b/src/components/common/ButtonCommon/ButtonCommon.module.scss index 32cf65655..318180ede 100644 --- a/src/components/common/ButtonCommon/ButtonCommon.module.scss +++ b/src/components/common/ButtonCommon/ButtonCommon.module.scss @@ -2,6 +2,32 @@ .buttonCommon { @apply shape-common; + &:hover { + .inner { + @apply shadow-md; + &:not(:disabled) { + filter: brightness(1.05); + } + } + } + + &:disabled { + cursor: not-allowed; + .inner { + filter: brightness(0.8) !important; + color: var(--disabled); + } + } + + &:focus { + outline: none; + .inner { + filter: brightness(1.05); + } + } + &:focus-visible { + outline: 2px solid var(--text-active); + } .inner { padding: 1rem 2rem; @apply bg-primary transition-all duration-200 text-white font-bold; @@ -14,37 +40,19 @@ @screen lg { padding: 1.6rem 3.2rem; } - &:disabled { - filter: brightness(0.9); - cursor: not-allowed; - color: var(--disabled); - } - &:hover { - @apply shadow-md; - &:not(:disabled) { - filter: brightness(1.05); - } - } - &:focus { - outline: none; - filter: brightness(1.05); - } - &:focus-visible { - outline: 2px solid var(--text-active); - } } &.loading { .inner { &::after { content: ""; border-radius: 50%; - width: 1.6rem; - height: 1.6rem; + width: 1.8rem; + height: 1.8rem; border: 3px solid rgba(170, 170, 170, 0.5); border-top: 3px solid var(--white); -webkit-animation: spin 2s linear infinite; animation: spin 2s linear infinite; - margin-right: 0.8rem; + margin-left: 0.8rem; } } } @@ -60,7 +68,7 @@ &.loading { .inner { &::after { - border-top-color: var(--primary); + border-top-color: var(--text-active); } } } @@ -87,7 +95,7 @@ } &.loading { .inner::after { - border-top-color: var(--text-active); + border-top-color: var(--primary); } } } @@ -105,14 +113,14 @@ } &.small { .inner { - padding: .5rem 1rem; + padding: 0.5rem 1rem; &.onlyIcon { padding: 1rem; } @screen md { - padding: .8rem 1.6rem; + padding: 0.8rem 1.6rem; &.onlyIcon { - padding: .8rem; + padding: 0.8rem; } } } diff --git a/src/components/common/ButtonCommon/ButtonCommon.tsx b/src/components/common/ButtonCommon/ButtonCommon.tsx index d83621d30..7261d6ff6 100644 --- a/src/components/common/ButtonCommon/ButtonCommon.tsx +++ b/src/components/common/ButtonCommon/ButtonCommon.tsx @@ -24,7 +24,7 @@ const ButtonCommon = memo(({ type = 'primary', size = 'default', loading = false [s.preserve]: isIconSuffix, [s.onlyIcon]: icon && !children, })} - disabled={disabled} + disabled={disabled || loading} onClick={onClick} >
diff --git a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx index afcfd259c..81d8f5687 100644 --- a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx +++ b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx @@ -28,6 +28,8 @@ const FormRegister = ({ onSwitch, isHide }: Props) => { const onSignup = () => { // TODO: validate fields signup({ email, password }) + // TODO: + alert("User created. Please verify your email") } From 4776ec6236d84a2a950e6b9ef536b30079033613 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 29 Sep 2021 12:05:05 +0700 Subject: [PATCH 05/16] :sparkles: feat: register with validation :%s --- package.json | 4 +- .../common/ButtonCommon/ButtonCommon.tsx | 69 ++++--- .../InputCommon/InputCommon.module.scss | 99 +--------- .../common/InputCommon/InputCommon.tsx | 176 +++++++++++------- .../InputFiledInForm.module.scss | 5 + .../InputFiledInForm/InputFiledInForm.tsx | 94 ++++++++++ .../InputPasswordFiledInForm.module.scss | 10 + .../InputPasswordFiledInForm.tsx | 49 +++++ .../components/FormRegister/FormRegister.tsx | 162 ++++++++++------ src/components/common/index.ts | 5 +- src/styles/_form.scss | 100 ++++++++++ src/styles/main.scss | 1 + yarn.lock | 79 +++++++- 13 files changed, 589 insertions(+), 264 deletions(-) create mode 100644 src/components/common/InputFiledInForm/InputFiledInForm.module.scss create mode 100644 src/components/common/InputFiledInForm/InputFiledInForm.tsx create mode 100644 src/components/common/InputPasswordFiledInForm/InputPasswordFiledInForm.module.scss create mode 100644 src/components/common/InputPasswordFiledInForm/InputPasswordFiledInForm.tsx create mode 100644 src/styles/_form.scss diff --git a/package.json b/package.json index bb78b5cf1..84a77cf71 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "email-validator": "^2.0.4", "eslint": "^7.32.0", "eslint-config-next": "^11.1.2", + "formik": "^2.2.9", "immutability-helper": "^3.1.1", "js-cookie": "^2.2.1", "lodash.debounce": "^4.0.8", @@ -51,7 +52,8 @@ "swr": "^0.5.6", "tabbable": "^5.2.0", "tailwindcss": "^2.2.2", - "uuidv4": "^6.2.10" + "uuidv4": "^6.2.10", + "yup": "^0.32.9" }, "devDependencies": { "@graphql-codegen/cli": "^1.21.5", diff --git a/src/components/common/ButtonCommon/ButtonCommon.tsx b/src/components/common/ButtonCommon/ButtonCommon.tsx index 7261d6ff6..862adf71d 100644 --- a/src/components/common/ButtonCommon/ButtonCommon.tsx +++ b/src/components/common/ButtonCommon/ButtonCommon.tsx @@ -3,38 +3,51 @@ import React, { memo } from 'react' import s from './ButtonCommon.module.scss' interface Props { - children?: React.ReactNode, - type?: 'primary' | 'light' | 'ghost' | 'lightBorderNone', - size?: 'default' | 'large' | 'small', - icon?: React.ReactNode, - isIconSuffix?: boolean, - loading?: boolean, - disabled?: boolean, - onClick?: () => void, + children?: React.ReactNode + type?: 'primary' | 'light' | 'ghost' | 'lightBorderNone' + HTMLType?: "submit" | "button" | "reset" + size?: 'default' | 'large' | 'small' + icon?: React.ReactNode + isIconSuffix?: boolean + loading?: boolean + disabled?: boolean + onClick?: () => void } -const ButtonCommon = memo(({ type = 'primary', size = 'default', loading = false, isIconSuffix = false, - icon, disabled, children, onClick }: Props) => { +const ButtonCommon = memo( + ({ + type = 'primary', + HTMLType, + size = 'default', + loading = false, + isIconSuffix = false, + icon, + disabled, + children, + onClick, + }: Props) => { return ( - + disabled={disabled || loading} + onClick={onClick} + type={HTMLType} + > +
+ {icon && {icon}} + {children} +
+ ) -}) + } +) +ButtonCommon.displayName = 'ButtonCommon' export default ButtonCommon diff --git a/src/components/common/InputCommon/InputCommon.module.scss b/src/components/common/InputCommon/InputCommon.module.scss index 5471b2169..b62048bb4 100644 --- a/src/components/common/InputCommon/InputCommon.module.scss +++ b/src/components/common/InputCommon/InputCommon.module.scss @@ -1,100 +1,5 @@ -@import "../../../styles/utilities"; +@import "../../../styles/form"; .inputWrap { - .inputInner { - @apply flex items-center relative; - .icon { - @apply absolute flex justify-center items-center; - content: ""; - left: 1.6rem; - margin-right: 1.6rem; - svg path { - fill: currentColor; - } - } - .icon + .inputCommon { - padding-left: 4.8rem; - } - - .inputCommon { - @apply block w-full transition-all duration-200 bg-white; - border-radius: .8rem; - padding: 1.6rem; - border: 1px solid var(--border-line); - &:hover, - &:focus, - &:active { - outline: none; - border: 1px solid var(--primary); - @apply shadow-md; - } - - &::placeholder { - @apply text-label; - } - } - - &.preserve { - @apply flex-row-reverse; - .icon { - left: unset; - right: 1.6rem; - margin-left: 1.6rem; - margin-right: 0; - svg path { - fill: var(--text-label); - } - } - .icon + .inputCommon { - padding-left: 1.6rem; - padding-right: 4.8rem; - } - } - &.success { - .icon { - svg path { - fill: var(--primary); - } - } - } - - &.error { - .icon { - svg path { - fill: var(--negative); - } - } - input { - border-color: var(--negative) !important; - } - } - } - .errorMessage { - @apply caption; - color: var(--negative); - margin-top: 0.4rem; - } - - &.custom { - @apply shape-common; - .inputCommon { - border: none; - background: var(--background-gray); - &:hover, - &:focus, - &:active { - @apply shadow-md; - border: none; - } - } - } - &.bgTransparent { - .inputCommon { - background: rgb(227, 242, 233, 0.3); - color: var(--white); - &::placeholder { - color: var(--white); - } - } - } + @extend .formInputWrap; } diff --git a/src/components/common/InputCommon/InputCommon.tsx b/src/components/common/InputCommon/InputCommon.tsx index 7ef9d886f..2300e9b80 100644 --- a/src/components/common/InputCommon/InputCommon.tsx +++ b/src/components/common/InputCommon/InputCommon.tsx @@ -1,95 +1,127 @@ -import classNames from 'classnames'; -import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react'; -import { IconCheck, IconError } from 'src/components/icons'; -import { KEY } from 'src/utils/constanst.utils'; -import s from './InputCommon.module.scss'; +import classNames from 'classnames' +import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react' +import { IconCheck, IconError } from 'src/components/icons' +import { KEY } from 'src/utils/constanst.utils' +import s from './InputCommon.module.scss' type Ref = { - focus: () => void - getValue: () => string | number -} | null; + focus: () => void + getValue: () => string | number +} | null interface Props { - children?: React.ReactNode, - value?: string | number, - placeholder?: string, - type?: 'text' | 'number' | 'email' | 'password', - styleType?: 'default' | 'custom', - backgroundTransparent?: boolean, - icon?: React.ReactNode, - isIconSuffix?: boolean, - isShowIconSuccess?: boolean, - error?: string, - onChange?: (value: string | number) => void, - onEnter?: (value: string | number) => void, + children?: React.ReactNode + value?: string | number + placeholder?: string + type?: 'text' | 'number' | 'email' | 'password' + styleType?: 'default' | 'custom' + backgroundTransparent?: boolean + icon?: React.ReactNode + isIconSuffix?: boolean + isShowIconSuccess?: boolean + error?: string + onChange?: (value: string | number) => void + onChangeEvent?: (e: React.ChangeEvent) => void + onBlur?: (e: any) => void + onEnter?: (value: string | number) => void } -const InputCommon = forwardRef(({ value, placeholder, type, styleType = 'default', icon, backgroundTransparent = false, - isIconSuffix, isShowIconSuccess, error, - onChange, onEnter }: Props, ref) => { - const inputElementRef = useRef(null); +const InputCommon = forwardRef( + ( + { + value, + placeholder, + type, + styleType = 'default', + icon, + backgroundTransparent = false, + isIconSuffix, + isShowIconSuccess, + error, + onChange, + onChangeEvent, + onEnter, + onBlur, + }: Props, + ref + ) => { + const inputElementRef = useRef(null) const iconElement = useMemo(() => { - if (error) { - return - } else if (isShowIconSuccess) { - return - } else if (icon) { - return {icon} - } - return <> + if (error) { + return ( + + {' '} + + ) + } else if (isShowIconSuccess) { + return ( + + {' '} + + ) + } else if (icon) { + return {icon} + } + return <> }, [icon, error, isShowIconSuccess]) useImperativeHandle(ref, () => ({ - focus: () => { - inputElementRef.current?.focus(); - }, - getValue: () => { - const value = inputElementRef.current?.value || '' - return value - } - })); + focus: () => { + inputElementRef.current?.focus() + }, + getValue: () => { + const value = inputElementRef.current?.value || '' + return value + }, + })) const handleChange = (e: React.ChangeEvent) => { + if (onChangeEvent) { + onChangeEvent(e) + } else { onChange && onChange(e.target.value) + } } const handleKeyDown = (e: any) => { - if (e.key === KEY.ENTER && onEnter) { - const value = inputElementRef.current?.value || '' - onEnter(value) - } + if (e.key === KEY.ENTER && onEnter) { + const value = inputElementRef.current?.value || '' + onEnter(value) + } } return ( -
-
- {iconElement} - -
- { - error &&
{error}
- } +
+
+ {iconElement} +
+ {error &&
{error}
} +
) + } +) -}) - +InputCommon.displayName = 'InputCommon' export default InputCommon diff --git a/src/components/common/InputFiledInForm/InputFiledInForm.module.scss b/src/components/common/InputFiledInForm/InputFiledInForm.module.scss new file mode 100644 index 000000000..b62048bb4 --- /dev/null +++ b/src/components/common/InputFiledInForm/InputFiledInForm.module.scss @@ -0,0 +1,5 @@ +@import "../../../styles/form"; + +.inputWrap { + @extend .formInputWrap; +} diff --git a/src/components/common/InputFiledInForm/InputFiledInForm.tsx b/src/components/common/InputFiledInForm/InputFiledInForm.tsx new file mode 100644 index 000000000..4b848e244 --- /dev/null +++ b/src/components/common/InputFiledInForm/InputFiledInForm.tsx @@ -0,0 +1,94 @@ +import classNames from 'classnames' +import { Field } from 'formik' +import React, { useMemo, useRef } from 'react' +import { IconCheck, IconError } from 'src/components/icons' +import { KEY } from 'src/utils/constanst.utils' +import s from './InputFiledInForm.module.scss' + +interface Props { + placeholder?: string + type?: 'text' | 'number' | 'email' | 'password' + styleType?: 'default' | 'custom' + backgroundTransparent?: boolean + icon?: React.ReactNode + isIconSuffix?: boolean + isShowIconSuccess?: boolean + name: string + error?: string + onChange?: (value: string | number) => void + onChangeEvent?: (e: React.ChangeEvent) => void + onBlur?: (e: any) => void + onEnter?: (value: string | number) => void +} + +const InputFiledInForm = ({ + name, + placeholder, + type, + styleType = 'default', + icon, + backgroundTransparent = false, + isIconSuffix = true, + isShowIconSuccess, + error, + onEnter, +}: Props) => { + const inputElementRef = useRef(null) + + const iconElement = useMemo(() => { + if (error) { + return ( + + {' '} + + ) + } else if (isShowIconSuccess) { + return ( + + {' '} + + ) + } else if (icon) { + return {icon} + } + return <> + }, [icon, error, isShowIconSuccess]) + + const handleKeyDown = (e: any) => { + if (e.key === KEY.ENTER && onEnter) { + const value = inputElementRef.current?.value || '' + onEnter(value) + } + } + + return ( +
+
+ {iconElement} + +
+ {error &&
{error}
} +
+ ) +} + +InputFiledInForm.displayName = 'InputFiledInForm' +export default InputFiledInForm diff --git a/src/components/common/InputPasswordFiledInForm/InputPasswordFiledInForm.module.scss b/src/components/common/InputPasswordFiledInForm/InputPasswordFiledInForm.module.scss new file mode 100644 index 000000000..598620891 --- /dev/null +++ b/src/components/common/InputPasswordFiledInForm/InputPasswordFiledInForm.module.scss @@ -0,0 +1,10 @@ +.iconPassword { + all: unset; + cursor: pointer; + &:focus { + outline: none; + } + &:focus-visible { + outline: 2px solid var(--text-active); + } +} diff --git a/src/components/common/InputPasswordFiledInForm/InputPasswordFiledInForm.tsx b/src/components/common/InputPasswordFiledInForm/InputPasswordFiledInForm.tsx new file mode 100644 index 000000000..3cdc48353 --- /dev/null +++ b/src/components/common/InputPasswordFiledInForm/InputPasswordFiledInForm.tsx @@ -0,0 +1,49 @@ +import React, { useState } from 'react' +import { IconPassword, IconPasswordCross } from 'src/components/icons' +import InputFiledInForm from '../InputFiledInForm/InputFiledInForm' +import s from './InputPasswordFiledInForm.module.scss' + +interface Props { + name?: string + placeholder?: string + styleType?: 'default' | 'custom' + error?: string + onChange?: (value: string | number) => void + onEnter?: (value: string | number) => void +} + +const InputPasswordFiledInForm = ({ + name = 'password', + placeholder, + styleType = 'default', + error, + onChange, + onEnter, +}: Props) => { + const [isShowPassword, setIsShowPassword] = useState(false) + const toggleShowPassword = (e: React.MouseEvent) => { + e.stopPropagation() + e.preventDefault() + setIsShowPassword(!isShowPassword) + } + + return ( + + {isShowPassword ? : } + + } + isIconSuffix={true} + onChange={onChange} + onEnter={onEnter} + /> + ) +} + +export default InputPasswordFiledInForm diff --git a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx index 81d8f5687..793218c7c 100644 --- a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx +++ b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx @@ -1,83 +1,121 @@ import classNames from 'classnames' -import React, { useEffect, useRef, useState } from 'react' -import { ButtonCommon, Inputcommon, InputPassword } from 'src/components/common' +import { Form, Formik } from 'formik' +import React, { useEffect, useRef } from 'react' +import { + ButtonCommon, + InputFiledInForm, + InputPasswordFiledInForm +} from 'src/components/common' import { CustomInputCommon } from 'src/utils/type.utils' +import * as Yup from 'yup' import { useSignup } from '../../../../hooks' import s from '../FormAuthen.module.scss' import SocialAuthen from '../SocialAuthen/SocialAuthen' import styles from './FormRegister.module.scss' interface Props { - isHide: boolean, - onSwitch: () => void + isHide: boolean + onSwitch: () => void } +const DisplayingErrorMessagesSchema = Yup.object().shape({ + email: Yup.string().email('Your email was wrong').required('Required'), + password: Yup.string() + .matches( + /^(?=.{8,})(?=.*[a-z])(?=.*[A-Z])((?=.*[0-9!@#$%^&*()\-_=+{};:,<.>]){1}).*$/, + 'Must contain 8 characters with at least 1 uppercase and 1 lowercase letter and either 1 number or 1 special character.' + ) + .max(30, 'Password is too long') + .required('Required'), +}) + const FormRegister = ({ onSwitch, isHide }: Props) => { - const emailRef = useRef(null) - const { loading, signup, error } = useSignup() - const [email, setEmail] = useState('') - const [password, setPassword] = useState('') + const emailRef = useRef(null) + const { loading, signup, error } = useSignup() - - useEffect(() => { - if (!isHide) { - emailRef.current?.focus() - } - }, [isHide]) - - const onSignup = () => { - // TODO: validate fields - signup({ email, password }) - // TODO: - alert("User created. Please verify your email") + useEffect(() => { + if (!isHide) { + emailRef.current?.focus() } + }, [isHide]) + const onSignup = (values: { email: string; password: string }) => { + signup({ email: values.email, password: values.password }) + // TODO: flow + alert('User created. Please verify your email') + } - useEffect(() => { - if (error) { - alert(error.message) - } - }, [error]) + useEffect(() => { + if (error) { + alert(error.message) + } + }, [error]) - return ( -
-
-
- setEmail(val.toString())} - /> - setPassword(val.toString())} - /> - -
- Must contain 8 characters with at least 1 uppercase and 1 lowercase letter and either 1 number or 1 special character. -
+ return ( +
+
+
+ + {({ errors, touched }) => ( +
+
+ + +
+ Must contain 8 characters with at least 1 uppercase and 1 + lowercase letter and either 1 number or 1 special character. +
- - Create Account - + + Create Account +
- -
- Already an account? - -
-
-
- ) + + )} + +
+ +
+ Already an account? + +
+
+
+ ) } -export default FormRegister \ No newline at end of file +export default FormRegister diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 7b82399f6..0edc064cc 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -1,3 +1,4 @@ +import { InputFiledInForm } from 'src/components/common/InputFiledInForm/InputFiledInForm'; export { default as ButtonCommon } from './ButtonCommon/ButtonCommon' export { default as Layout } from './Layout/Layout' export { default as CarouselCommon } from './CarouselCommon/CarouselCommon' @@ -47,4 +48,6 @@ export { default as StaticImage} from './StaticImage/StaticImage' export { default as EmptyCommon} from './EmptyCommon/EmptyCommon' export { default as CustomShapeSvg} from './CustomShapeSvg/CustomShapeSvg' export { default as RecommendedRecipes} from './RecommendedRecipes/RecommendedRecipes' -export { default as LayoutCheckout} from './LayoutCheckout/LayoutCheckout' \ No newline at end of file +export { default as LayoutCheckout} from './LayoutCheckout/LayoutCheckout' +export { default as InputPasswordFiledInForm} from './InputPasswordFiledInForm/InputPasswordFiledInForm' +export { default as InputFiledInForm} from './InputFiledInForm/InputFiledInForm' diff --git a/src/styles/_form.scss b/src/styles/_form.scss new file mode 100644 index 000000000..0ec3b4a96 --- /dev/null +++ b/src/styles/_form.scss @@ -0,0 +1,100 @@ +@import './utilities'; + +.formInputWrap { + .inputInner { + @apply flex items-center relative; + .icon { + @apply absolute flex justify-center items-center; + content: ""; + left: 1.6rem; + margin-right: 1.6rem; + svg path { + fill: currentColor; + } + } + .icon + input { + padding-left: 4.8rem; + } + + input { + @apply block w-full transition-all duration-200 bg-white; + border-radius: .8rem; + padding: 1.6rem; + border: 1px solid var(--border-line); + &:hover, + &:focus, + &:active { + outline: none; + border: 1px solid var(--primary); + @apply shadow-md; + } + + &::placeholder { + @apply text-label; + } + } + + &.preserve { + @apply flex-row-reverse; + .icon { + left: unset; + right: 1.6rem; + margin-left: 1.6rem; + margin-right: 0; + svg path { + fill: var(--text-label); + } + } + .icon + input { + padding-left: 1.6rem; + padding-right: 4.8rem; + } + } + &.success { + .icon { + svg path { + fill: var(--primary); + } + } + } + + &.error { + .icon { + svg path { + fill: var(--negative); + } + } + input { + border-color: var(--negative) !important; + } + } + } + .errorMessage { + @apply caption; + color: var(--negative); + margin-top: 0.4rem; + } + + &.custom { + @apply shape-common; + input { + border: none; + background: var(--background-gray); + &:hover, + &:focus, + &:active { + @apply shadow-md; + border: none; + } + } + } + &.bgTransparent { + input { + background: rgb(227, 242, 233, 0.3); + color: var(--white); + &::placeholder { + color: var(--white); + } + } + } + } \ No newline at end of file diff --git a/src/styles/main.scss b/src/styles/main.scss index 51391b33f..e742ca4be 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -6,4 +6,5 @@ @import "~tailwindcss/utilities"; @import './utilities'; +@import './form'; @import './pages' diff --git a/yarn.lock b/yarn.lock index b69068411..5964ce61a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -445,7 +445,7 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.14.0": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.14.0": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== @@ -1213,6 +1213,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.173.tgz#9d3b674c67a26cf673756f6aca7b429f237f91ed" integrity sha512-vv0CAYoaEjCw/mLy96GBTnRoZrSxkGE0BKzKimdR8P3OzrNYNvBgtW7p055A+E8C31vXNUhWKoFCbhq7gbyhFg== +"@types/lodash@^4.14.165": + version "4.14.175" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.175.tgz#b78dfa959192b01fae0ad90e166478769b215f45" + integrity sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw== + "@types/lru-cache@4.1.1": version "4.1.1" resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-4.1.1.tgz#b2d87a5e3df8d4b18ca426c5105cd701c2306d40" @@ -2572,6 +2577,11 @@ deepmerge@4.2.2, deepmerge@^4.0.0, deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +deepmerge@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170" + integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -3279,6 +3289,19 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +formik@^2.2.9: + version "2.2.9" + resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0" + integrity sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA== + dependencies: + deepmerge "^2.1.1" + hoist-non-react-statics "^3.3.0" + lodash "^4.17.21" + lodash-es "^4.17.21" + react-fast-compare "^2.0.1" + tiny-warning "^1.0.2" + tslib "^1.10.0" + fraction.js@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.1.1.tgz#ac4e520473dae67012d618aab91eda09bcb400ff" @@ -3595,6 +3618,13 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +hoist-non-react-statics@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -4415,6 +4445,11 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash-es@^4.17.15, lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -4752,6 +4787,11 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +nanoclone@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" + integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== + nanoid@^3.1.23: version "3.1.25" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" @@ -5795,6 +5835,11 @@ prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +property-expr@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910" + integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg== + public-encrypt@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" @@ -5921,6 +5966,11 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +react-fast-compare@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + react-fast-compare@^3.0.1: version "3.2.0" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" @@ -5936,7 +5986,7 @@ react-is@17.0.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^16.8.1: +react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -6878,6 +6928,11 @@ timers-browserify@2.0.12, timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + title-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" @@ -6926,6 +6981,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= + totalist@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" @@ -6970,7 +7030,7 @@ tsconfig-paths@^3.11.0, tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -7419,3 +7479,16 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yup@^0.32.9: + version "0.32.9" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.9.tgz#9367bec6b1b0e39211ecbca598702e106019d872" + integrity sha512-Ci1qN+i2H0XpY7syDQ0k5zKQ/DoxO0LzPg8PAR/X4Mpj6DqaeCoIYEEjDJwhArh3Fa7GWbQQVDZKeXYlSH4JMg== + dependencies: + "@babel/runtime" "^7.10.5" + "@types/lodash" "^4.14.165" + lodash "^4.17.20" + lodash-es "^4.17.15" + nanoclone "^0.2.1" + property-expr "^2.0.4" + toposort "^2.0.2" From 02cc226ba7bd2added5e1cc8bb8f52567a06f525 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 29 Sep 2021 13:39:40 +0700 Subject: [PATCH 06/16] :art: styles: message common :%s --- pages/test.tsx | 42 +++++++---- src/components/common/Banner/Banner.tsx | 1 + .../MessageCommon/MessageCommon.module.scss | 7 ++ .../common/MessageCommon/MessageCommon.tsx | 26 +++++++ .../MessageItem/MessageItem.module.scss | 64 +++++++++++++++++ .../MessageCommon/MessageItem/MessageItem.tsx | 71 +++++++++++++++++++ src/components/common/index.ts | 4 +- 7 files changed, 201 insertions(+), 14 deletions(-) create mode 100644 src/components/common/MessageCommon/MessageCommon.module.scss create mode 100644 src/components/common/MessageCommon/MessageCommon.tsx create mode 100644 src/components/common/MessageCommon/MessageItem/MessageItem.module.scss create mode 100644 src/components/common/MessageCommon/MessageItem/MessageItem.tsx diff --git a/pages/test.tsx b/pages/test.tsx index b40ad354a..e94bba73d 100644 --- a/pages/test.tsx +++ b/pages/test.tsx @@ -1,19 +1,35 @@ -import { useEffect, useState } from 'react' -import { - ButtonCommon, Layout -} from 'src/components/common' +import { Layout, MessageCommon } from 'src/components/common' +import { MessageItemProps } from 'src/components/common/MessageCommon/MessageItem/MessageItem' + +const data: MessageItemProps[] = [ + { + id: 1, + content: 'Create account successfully', + type: 'error', + }, + { + id: 2, + content: 'Create account successfully', + type: 'success', + }, + { + id: 3, + content: 'Create account successfully', + type: 'warning', + }, + { + id: 4, + content: 'Create account successfully', + type: 'info', + }, +] export default function Test() { - const [isLoading, setisLoading] = useState(false) - useEffect(() => { - setTimeout(() => { - setisLoading(true) - }, 3000) - }, []) return ( <> - Back to home - Back to home - Back to home + + {/* Create account successfully + Create account successfully + Create account successfully */} ) } diff --git a/src/components/common/Banner/Banner.tsx b/src/components/common/Banner/Banner.tsx index dc95c2146..8a98fe3a6 100644 --- a/src/components/common/Banner/Banner.tsx +++ b/src/components/common/Banner/Banner.tsx @@ -41,4 +41,5 @@ const Banner = memo(({ data }: Props) => { ) }) +Banner.displayName = 'Banner' export default Banner diff --git a/src/components/common/MessageCommon/MessageCommon.module.scss b/src/components/common/MessageCommon/MessageCommon.module.scss new file mode 100644 index 000000000..99137e828 --- /dev/null +++ b/src/components/common/MessageCommon/MessageCommon.module.scss @@ -0,0 +1,7 @@ +.messageCommon { + @apply fixed; + top: 2.4rem; + left: 50%; + z-index: 20000; + transform: translateX(-50%); +} diff --git a/src/components/common/MessageCommon/MessageCommon.tsx b/src/components/common/MessageCommon/MessageCommon.tsx new file mode 100644 index 000000000..8ffd62e35 --- /dev/null +++ b/src/components/common/MessageCommon/MessageCommon.tsx @@ -0,0 +1,26 @@ +import React, { memo } from 'react' +import s from './MessageCommon.module.scss' +import MessageItem, { MessageItemProps } from './MessageItem/MessageItem' + +interface Props { + messages: MessageItemProps[] + onRemove?: (id?: number) => void +} + +const MessageCommon = memo(({ messages, onRemove }: Props) => { + return ( +
+ {messages.map((item) => ( + + ))} +
+ ) +}) + +MessageCommon.displayName = 'MessageCommon' +export default MessageCommon diff --git a/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss b/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss new file mode 100644 index 000000000..da9896d05 --- /dev/null +++ b/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss @@ -0,0 +1,64 @@ +@import "../../../../styles/utilities"; + +.messageItem { + @apply shadow-sm flex justify-center items-center cursor-default; + width: fit-content; + padding: 0.8rem 2.4rem; + margin-bottom: .8rem; + border-radius: 0.8rem; + transition: all .5s; + animation: showMessage .5s; + + &.hide { + display: none; + } + .icon { + @apply flex justify-center items-center; + margin-right: 0.8rem; + svg { + width: 2rem; + height: 2rem; + } + } + &.info { + @apply bg-info-light; + color: var(--info-dark); + .icon svg path { + fill: var(--info); + } + } + &.success { + @apply bg-positive-light; + color: var(--positive-dark); + .icon svg path { + fill: var(--positive); + } + } + &.error { + @apply bg-negative-light; + color: var(--negative-dark); + .icon svg path { + fill: var(--negative); + } + } + &.warning { + @apply bg-warning-light; + color: var(--warning-dark); + .icon svg path { + fill: var(--warning); + } + } +} + + +@keyframes showMessage { + 0% { + transform: translateY(-6rem); + opacity: .5; + } + + 100% { + transform: none; + opacity: 1; + } +} diff --git a/src/components/common/MessageCommon/MessageItem/MessageItem.tsx b/src/components/common/MessageCommon/MessageItem/MessageItem.tsx new file mode 100644 index 000000000..38d7ed180 --- /dev/null +++ b/src/components/common/MessageCommon/MessageItem/MessageItem.tsx @@ -0,0 +1,71 @@ +import classNames from 'classnames' +import React, { memo, useEffect, useMemo, useState } from 'react' +import { IconCheck, IconError, IconInfo } from 'src/components/icons' +import s from './MessageItem.module.scss' + +export interface MessageItemProps { + id?: number + content?: React.ReactNode + type?: 'info' | 'success' | 'error' | 'warning' + timeout?: number + onRemove?: (id?: number) => void +} + +const MessageItem = memo( + ({ id, content, type = 'success', timeout = 3000, onRemove }: MessageItemProps) => { + const [isHide, setIsHide] = useState() + const [isMouseOver, setIsMouseOver] = useState(false) + + const iconElement = useMemo(() => { + switch (type) { + case 'info': + return + case 'success': + return + case 'error': + return + case 'warning': + return + default: + return + } + }, [type]) + + useEffect(() => { + setIsHide(false) + setTimeout(() => { + setIsHide(true) + }, timeout) + }, [timeout, isMouseOver]) + + useEffect(() => { + if (isHide && !isMouseOver && onRemove) { + onRemove(id) + } + }, [isHide, isMouseOver, onRemove, id]) + + const onMouseOver = () => { + setIsMouseOver(true) + } + + const onMouseLeave = () => { + setIsMouseOver(false) + } + + return ( +
+ {iconElement} + {content} +
+ ) + } +) + +MessageItem.displayName = 'MessageItem' +export default MessageItem diff --git a/src/components/common/index.ts b/src/components/common/index.ts index 0edc064cc..eaa33176c 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -1,4 +1,3 @@ -import { InputFiledInForm } from 'src/components/common/InputFiledInForm/InputFiledInForm'; export { default as ButtonCommon } from './ButtonCommon/ButtonCommon' export { default as Layout } from './Layout/Layout' export { default as CarouselCommon } from './CarouselCommon/CarouselCommon' @@ -51,3 +50,6 @@ export { default as RecommendedRecipes} from './RecommendedRecipes/RecommendedRe export { default as LayoutCheckout} from './LayoutCheckout/LayoutCheckout' export { default as InputPasswordFiledInForm} from './InputPasswordFiledInForm/InputPasswordFiledInForm' export { default as InputFiledInForm} from './InputFiledInForm/InputFiledInForm' +export { default as MessageCommon} from './MessageCommon/MessageCommon' + + From ba6517ef39885c07d07726bcdd8716f25df80de6 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 29 Sep 2021 14:23:42 +0700 Subject: [PATCH 07/16] :sparkles: feat: show message common :%s --- pages/test.tsx | 37 +++------- src/components/common/Layout/Layout.tsx | 27 ++++---- .../Layout/LayoutContent/LayoutContent.tsx | 5 +- .../common/MessageCommon/MessageCommon.tsx | 16 +++-- .../MessageItem/MessageItem.module.scss | 2 +- .../MessageCommon/MessageItem/MessageItem.tsx | 4 +- .../contexts/Message/MessageContext.tsx | 27 ++++++++ .../contexts/Message/MessageProvider.tsx | 67 +++++++++++++++++++ src/components/contexts/index.ts | 5 +- 9 files changed, 139 insertions(+), 51 deletions(-) create mode 100644 src/components/contexts/Message/MessageContext.tsx create mode 100644 src/components/contexts/Message/MessageProvider.tsx diff --git a/pages/test.tsx b/pages/test.tsx index e94bba73d..a140d5aee 100644 --- a/pages/test.tsx +++ b/pages/test.tsx @@ -1,35 +1,16 @@ -import { Layout, MessageCommon } from 'src/components/common' -import { MessageItemProps } from 'src/components/common/MessageCommon/MessageItem/MessageItem' +import { Layout } from 'src/components/common' +import { useMessage } from 'src/components/contexts' -const data: MessageItemProps[] = [ - { - id: 1, - content: 'Create account successfully', - type: 'error', - }, - { - id: 2, - content: 'Create account successfully', - type: 'success', - }, - { - id: 3, - content: 'Create account successfully', - type: 'warning', - }, - { - id: 4, - content: 'Create account successfully', - type: 'info', - }, -] export default function Test() { + const { showMessageSuccess } = useMessage() + + const handleClick = () => { + showMessageSuccess("Create account successfully") + } + return ( <> - - {/* Create account successfully - Create account successfully - Create account successfully */} + ) } diff --git a/src/components/common/Layout/Layout.tsx b/src/components/common/Layout/Layout.tsx index e7b0c88e4..ec5a1646d 100644 --- a/src/components/common/Layout/Layout.tsx +++ b/src/components/common/Layout/Layout.tsx @@ -1,25 +1,24 @@ import { CommerceProvider } from '@framework' import { useRouter } from 'next/router' import { FC } from 'react' -import { CartDrawerProvider } from 'src/components/contexts' +import { CartDrawerProvider, MessageProvider } from 'src/components/contexts' import LayoutContent from './LayoutContent/LayoutContent' interface Props { - className?: string - children?: any + className?: string + children?: any } const Layout: FC = ({ children }) => { - const { locale = 'en-US' } = useRouter() - return ( - - - - {children} - - - - - ) + const { locale = 'en-US' } = useRouter() + return ( + + + + {children} + + + + ) } export default Layout diff --git a/src/components/common/Layout/LayoutContent/LayoutContent.tsx b/src/components/common/Layout/LayoutContent/LayoutContent.tsx index 7c096be0a..77eac0a2a 100644 --- a/src/components/common/Layout/LayoutContent/LayoutContent.tsx +++ b/src/components/common/Layout/LayoutContent/LayoutContent.tsx @@ -1,8 +1,9 @@ import { useRouter } from 'next/router' import { FC } from 'react' +import { useMessage } from 'src/components/contexts' import { useModalCommon } from 'src/components/hooks' import { BRAND, CATEGORY, FEATURED, FILTER_PAGE, ROUTE } from 'src/utils/constanst.utils' -import { CartDrawer, Footer, ScrollToTop } from '../..' +import { CartDrawer, Footer, MessageCommon, ScrollToTop } from '../..' import Header from '../../Header/Header' import MenuNavigationProductList from '../../MenuNavigationProductList/MenuNavigationProductList' import s from './LayoutContent.module.scss' @@ -16,6 +17,7 @@ const LayoutContent: FC = ({ children }) => { const { pathname } = useRouter() const { visible: visibleFilter, openModal: openFilter, closeModal: closeFilter } = useModalCommon({ initialValue: false }) const router = useRouter() + const {messages, removeMessage} = useMessage() const toggleFilter = () => { if (visibleFilter) { @@ -44,6 +46,7 @@ const LayoutContent: FC = ({ children }) => {
+ ) diff --git a/src/components/common/MessageCommon/MessageCommon.tsx b/src/components/common/MessageCommon/MessageCommon.tsx index 8ffd62e35..adda6a040 100644 --- a/src/components/common/MessageCommon/MessageCommon.tsx +++ b/src/components/common/MessageCommon/MessageCommon.tsx @@ -1,21 +1,29 @@ -import React, { memo } from 'react' +import React, { memo, useEffect } from 'react' import s from './MessageCommon.module.scss' import MessageItem, { MessageItemProps } from './MessageItem/MessageItem' interface Props { messages: MessageItemProps[] - onRemove?: (id?: number) => void + onRemove?: (id: number) => void } const MessageCommon = memo(({ messages, onRemove }: Props) => { + useEffect(() => { + console.log("this fun change; onRemove") + }, [onRemove]) + + const handleRemove = (id: number) => { + onRemove && onRemove(id) + } + return (
- {messages.map((item) => ( + {messages.reverse().map((item) => ( ))}
diff --git a/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss b/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss index da9896d05..e1f2d1b49 100644 --- a/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss +++ b/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss @@ -53,7 +53,7 @@ @keyframes showMessage { 0% { - transform: translateY(-6rem); + transform: translateY(-2rem); opacity: .5; } diff --git a/src/components/common/MessageCommon/MessageItem/MessageItem.tsx b/src/components/common/MessageCommon/MessageItem/MessageItem.tsx index 38d7ed180..89f9b6600 100644 --- a/src/components/common/MessageCommon/MessageItem/MessageItem.tsx +++ b/src/components/common/MessageCommon/MessageItem/MessageItem.tsx @@ -8,7 +8,7 @@ export interface MessageItemProps { content?: React.ReactNode type?: 'info' | 'success' | 'error' | 'warning' timeout?: number - onRemove?: (id?: number) => void + onRemove?: (id: number) => void } const MessageItem = memo( @@ -40,7 +40,7 @@ const MessageItem = memo( useEffect(() => { if (isHide && !isMouseOver && onRemove) { - onRemove(id) + onRemove(id || 0) } }, [isHide, isMouseOver, onRemove, id]) diff --git a/src/components/contexts/Message/MessageContext.tsx b/src/components/contexts/Message/MessageContext.tsx new file mode 100644 index 000000000..f9399498e --- /dev/null +++ b/src/components/contexts/Message/MessageContext.tsx @@ -0,0 +1,27 @@ +import { createContext, useContext } from 'react' +import { MessageItemProps } from 'src/components/common/MessageCommon/MessageItem/MessageItem' + +export type MessageContextType = { + messages: MessageItemProps[] + removeMessage: (id: number) => void + showMessageSuccess: (content: string, timeout?: number) => void + showMessageInfo: (content: string, timeout?: number) => void + showMessageError: (content: string, timeout?: number) => void + showMessageWarning: (content: string, timeout?: number) => void +} +export const DEFAULT_MESSAGE_CONTEXT: MessageContextType = { + messages: [], + removeMessage: () => {}, + showMessageSuccess: () => {}, + showMessageInfo: () => {}, + showMessageError: () => {}, + showMessageWarning: () => {}, +} + +export const MessageContext = createContext( + DEFAULT_MESSAGE_CONTEXT +) + +export function useMessage() { + return useContext(MessageContext) +} diff --git a/src/components/contexts/Message/MessageProvider.tsx b/src/components/contexts/Message/MessageProvider.tsx new file mode 100644 index 000000000..854fb5ac6 --- /dev/null +++ b/src/components/contexts/Message/MessageProvider.tsx @@ -0,0 +1,67 @@ +import { ReactNode, useCallback, useState } from 'react' +import { MessageItemProps } from 'src/components/common/MessageCommon/MessageItem/MessageItem' +import { MessageContext } from './MessageContext' + +type Props = { + children: ReactNode +} + +export function MessageProvider({ children }: Props) { + const [currentId, setCurrentId] = useState(0) + const [messages, setMessages] = useState([]) + + const createNewMessage = ( + content: string, + timeout?: number, + type?: 'info' | 'success' | 'error' | 'warning' + ) => { + const item: MessageItemProps = { + id: currentId + 1, + content: currentId + 1 + content, + type, + timeout, + } + setCurrentId(currentId + 1) + setMessages([...messages, item]) + } + + const showMessageSuccess = (content: string, timeout?: number) => { + createNewMessage(content, timeout, 'success') + } + + const showMessageInfo = (content: string, timeout?: number) => { + createNewMessage(content, timeout, 'info') + } + + const showMessageError = (content: string, timeout?: number) => { + createNewMessage(content, timeout, 'error') + } + + const showMessageWarning = (content: string, timeout?: number) => { + createNewMessage(content, timeout, 'warning') + } + + const removeMessage = (id: number) => { + const newMessages = messages.filter((item) => item.id !== id) + setMessages(newMessages) + + if (newMessages.length === 0) { + setCurrentId(0) + } + } + + return ( + + {children} + + ) +} diff --git a/src/components/contexts/index.ts b/src/components/contexts/index.ts index a34c91d85..795c9161c 100644 --- a/src/components/contexts/index.ts +++ b/src/components/contexts/index.ts @@ -1,2 +1,5 @@ export * from './CartDrawer/CartDrawerContext' -export * from './CartDrawer/CartDrawerProvider' \ No newline at end of file +export * from './CartDrawer/CartDrawerProvider' + +export * from './Message/MessageContext' +export * from './Message/MessageProvider' From 275f10c5e71caec47de19781a811d12b2612e962 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 29 Sep 2021 14:40:06 +0700 Subject: [PATCH 08/16] :sparkles: feat: register flow :%s --- .../common/MessageCommon/MessageCommon.tsx | 12 ++--------- .../MessageItem/MessageItem.module.scss | 2 ++ .../components/FormRegister/FormRegister.tsx | 21 +++++++++++++------ .../contexts/Message/MessageProvider.tsx | 2 +- src/components/hooks/useLogin.tsx | 1 + src/components/hooks/useSignup.tsx | 13 +++++++++--- src/components/hooks/useVerifyCustomer.tsx | 10 +++------ src/utils/language.utils.ts | 3 +++ 8 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/components/common/MessageCommon/MessageCommon.tsx b/src/components/common/MessageCommon/MessageCommon.tsx index adda6a040..f53469c50 100644 --- a/src/components/common/MessageCommon/MessageCommon.tsx +++ b/src/components/common/MessageCommon/MessageCommon.tsx @@ -7,15 +7,7 @@ interface Props { onRemove?: (id: number) => void } -const MessageCommon = memo(({ messages, onRemove }: Props) => { - useEffect(() => { - console.log("this fun change; onRemove") - }, [onRemove]) - - const handleRemove = (id: number) => { - onRemove && onRemove(id) - } - +const MessageCommon = memo(({ messages, onRemove }: Props) => { return (
{messages.reverse().map((item) => ( @@ -23,7 +15,7 @@ const MessageCommon = memo(({ messages, onRemove }: Props) => { key={item.id} id={item.id} content={item.content} - onRemove={handleRemove} + onRemove={onRemove} /> ))}
diff --git a/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss b/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss index e1f2d1b49..f2583810d 100644 --- a/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss +++ b/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss @@ -8,6 +8,8 @@ border-radius: 0.8rem; transition: all .5s; animation: showMessage .5s; + width: max-content; + margin: 0 1.6rem; &.hide { display: none; diff --git a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx index 793218c7c..1cccf6977 100644 --- a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx +++ b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx @@ -2,10 +2,12 @@ import classNames from 'classnames' import { Form, Formik } from 'formik' import React, { useEffect, useRef } from 'react' import { - ButtonCommon, - InputFiledInForm, - InputPasswordFiledInForm + ButtonCommon, + InputFiledInForm, + InputPasswordFiledInForm, } from 'src/components/common' +import { useMessage } from 'src/components/contexts' +import { LANGUAGE } from 'src/utils/language.utils' import { CustomInputCommon } from 'src/utils/type.utils' import * as Yup from 'yup' import { useSignup } from '../../../../hooks' @@ -32,6 +34,7 @@ const DisplayingErrorMessagesSchema = Yup.object().shape({ const FormRegister = ({ onSwitch, isHide }: Props) => { const emailRef = useRef(null) const { loading, signup, error } = useSignup() + const { showMessageSuccess, showMessageError } = useMessage() useEffect(() => { if (!isHide) { @@ -40,9 +43,15 @@ const FormRegister = ({ onSwitch, isHide }: Props) => { }, [isHide]) const onSignup = (values: { email: string; password: string }) => { - signup({ email: values.email, password: values.password }) - // TODO: flow - alert('User created. Please verify your email') + signup({ email: values.email, password: values.password }, onSignupCallBack) + } + + const onSignupCallBack = (isSuccess: boolean, message?: string) => { + if (isSuccess) { + showMessageSuccess("Create account successfully. Please verify your email to login.") + } else { + showMessageError(message || LANGUAGE.MESSAGE.ERROR) + } } useEffect(() => { diff --git a/src/components/contexts/Message/MessageProvider.tsx b/src/components/contexts/Message/MessageProvider.tsx index 854fb5ac6..0ccde8609 100644 --- a/src/components/contexts/Message/MessageProvider.tsx +++ b/src/components/contexts/Message/MessageProvider.tsx @@ -17,7 +17,7 @@ export function MessageProvider({ children }: Props) { ) => { const item: MessageItemProps = { id: currentId + 1, - content: currentId + 1 + content, + content, type, timeout, } diff --git a/src/components/hooks/useLogin.tsx b/src/components/hooks/useLogin.tsx index 0e056c2f7..5a000b9b1 100644 --- a/src/components/hooks/useLogin.tsx +++ b/src/components/hooks/useLogin.tsx @@ -42,6 +42,7 @@ const useLogin = () => { throw CommonError.create(data.login.message, data.login.errorCode) } const authToken = headers.get('vendure-auth-token') + console.log("auth token: ", authToken) if (authToken != null) { localStorage.setItem('token', authToken) return mutate() diff --git a/src/components/hooks/useSignup.tsx b/src/components/hooks/useSignup.tsx index 91a5fe186..4842915e1 100644 --- a/src/components/hooks/useSignup.tsx +++ b/src/components/hooks/useSignup.tsx @@ -32,7 +32,10 @@ const useSignup = () => { const [error, setError] = useState(null) const { mutate } = useActiveCustomer() - const signup = ({ firstName, lastName, email, password }: SignupInput) => { + const signup = ( + { firstName, lastName, email, password }: SignupInput, + fCallBack: (isSuccess: boolean, message?: string) => void + ) => { setError(null) setLoading(true) fetcher({ @@ -53,11 +56,15 @@ const useSignup = () => { data.registerCustomerAccount.errorCode ) } - console.log(data) + mutate() + fCallBack(true) return data }) - .catch(setError) + .catch((error) => { + setError(error) + fCallBack(false, error.message) + }) .finally(() => setLoading(false)) } diff --git a/src/components/hooks/useVerifyCustomer.tsx b/src/components/hooks/useVerifyCustomer.tsx index a4af3184e..d86a5b8ed 100644 --- a/src/components/hooks/useVerifyCustomer.tsx +++ b/src/components/hooks/useVerifyCustomer.tsx @@ -25,7 +25,7 @@ const useVerifyCustomer = () => { query: VERIFY_CUSTOMER_ACCOUNT, variables: options, }) - .then(({ data, headers }) => { + .then(({ data }) => { if (data.verifyCustomerAccount.__typename !== 'CurrentUser') { throw CommonError.create( data.verifyCustomerAccount.message, @@ -33,12 +33,8 @@ const useVerifyCustomer = () => { ) } fCallBack && fCallBack(true) - - const authToken = headers.get('vendure-auth-token') - if (authToken != null) { - localStorage.setItem('token', authToken) - return mutate() - } + mutate() + return data }) .catch((err) => { setError(err) diff --git a/src/utils/language.utils.ts b/src/utils/language.utils.ts index dfd2b9604..921c29435 100644 --- a/src/utils/language.utils.ts +++ b/src/utils/language.utils.ts @@ -9,5 +9,8 @@ export const LANGUAGE = { }, PLACE_HOLDER: { SEARCH: 'Search', + }, + MESSAGE: { + ERROR: 'Something went wrong! Please try again!' } } \ No newline at end of file From 660ba8d301adb7a3fadf182a2206da52727120fa Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 29 Sep 2021 14:48:12 +0700 Subject: [PATCH 09/16] :art: styles: page verify customer account :%s --- .../VerifyCustomerAccount.module.scss | 11 ++++++ .../VerifyCustomerAccount.tsx | 34 +++++++++++++++---- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.module.scss b/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.module.scss index c0abfceda..64431e892 100644 --- a/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.module.scss +++ b/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.module.scss @@ -12,5 +12,16 @@ .message { margin-bottom: 1.6rem; } + .bottom { + @apply flex justify-center items-center flex-col; + a { + margin-right: 1.6rem; + margin-bottom: 1.6rem; + width: 100%; + } + button { + @apply w-full; + } + } } } diff --git a/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx b/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx index 75caecb5b..e2c93db2e 100644 --- a/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx +++ b/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx @@ -2,15 +2,22 @@ import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import { ButtonCommon } from 'src/components/common' import LoadingCommon from 'src/components/common/LoadingCommon/LoadingCommon' -import { useVerifyCustomer } from 'src/components/hooks' +import { useModalCommon, useVerifyCustomer } from 'src/components/hooks' import { ROUTE } from 'src/utils/constanst.utils' import s from './VerifyCustomerAccount.module.scss' import Link from 'next/link' +import { LANGUAGE } from 'src/utils/language.utils' +import ModalAuthenticate from 'src/components/common/ModalAuthenticate/ModalAuthenticate' export default function VerifyCustomerAccount() { const router = useRouter() const [isVerified, setIsVerified] = useState(false) const { error, loading, verify } = useVerifyCustomer() + const { + visible: visibleModalAuthen, + closeModal: closeModalAuthen, + openModal: openModalAuthen, + } = useModalCommon({ initialValue: false }) useEffect(() => { const token = router.query.token @@ -37,14 +44,27 @@ export default function VerifyCustomerAccount() {
) : (
-
Congratulation! Verified account successfully
- - - Back to home - - +
+ Congratulation! Verified account successfully +
+
+ + + Back to home + + + + + {LANGUAGE.BUTTON_LABEL.SIGNIN} + +
)} + +
) } From faabf5228d09f723d93d066d79c7092105701ce9 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 29 Sep 2021 16:58:12 +0700 Subject: [PATCH 10/16] :sparkles: feat: logout :%s --- pages/account.tsx | 19 +- .../components/HeaderMenu/HeaderMenu.tsx | 239 ++++++++++-------- .../MenuNavigationProductList.tsx | 2 +- src/components/hooks/auth/index.ts | 3 + src/components/hooks/auth/useLogout.tsx | 34 +++ src/components/hooks/useLogin.tsx | 4 +- .../account/AccountPage/AccountPage.tsx | 2 +- .../EditInfoModal/EditInfoModal.tsx | 2 - .../FormSubscribe/FormSubscribe.tsx | 1 - src/utils/constanst.utils.ts | 4 + src/utils/fetcher.ts | 7 +- src/utils/rawFetcher.ts | 5 +- 12 files changed, 201 insertions(+), 121 deletions(-) create mode 100644 src/components/hooks/auth/index.ts create mode 100644 src/components/hooks/auth/useLogout.tsx diff --git a/pages/account.tsx b/pages/account.tsx index 7da102c52..df4121aa7 100644 --- a/pages/account.tsx +++ b/pages/account.tsx @@ -1,13 +1,16 @@ -import React from 'react'; -import { Layout } from 'src/components/common'; -import { AccountPage } from 'src/components/modules/account'; +import React from 'react' +import { Layout } from 'src/components/common' +import useActiveCustomer from 'src/components/hooks/useActiveCustomer' +import { AccountPage, AccountSignIn } from 'src/components/modules/account' const Account = () => { - return ( - - ); -}; + const { customer } = useActiveCustomer() + if (customer) { + return + } + return +} Account.Layout = Layout -export default Account; \ No newline at end of file +export default Account diff --git a/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx b/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx index 2060d5996..c29890dbd 100644 --- a/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx +++ b/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx @@ -6,115 +6,154 @@ import { ButtonCommon } from 'src/components/common' import InputSearch from 'src/components/common/InputSearch/InputSearch' import MenuDropdown from 'src/components/common/MenuDropdown/MenuDropdown' import { useCartDrawer } from 'src/components/contexts' -import { IconBuy, IconFilter, IconHeart, IconHistory, IconUser } from 'src/components/icons' -import { ACCOUNT_TAB, FILTER_PAGE, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils' +import { + IconBuy, + IconFilter, + IconHeart, + IconHistory, + IconUser, +} from 'src/components/icons' +import { + ACCOUNT_TAB, + FILTER_PAGE, + QUERY_KEY, + ROUTE, +} from 'src/utils/constanst.utils' import Logo from '../../../Logo/Logo' import s from './HeaderMenu.module.scss' +import { useLogout } from '../../../../hooks/auth' interface Props { - children?: any, - isFull?: boolean, - isStickyHeader?: boolean, - visibleFilter?: boolean, - openModalAuthen: () => void, - openModalInfo: () => void, - toggleFilter: () => void, + children?: any + isFull?: boolean + isStickyHeader?: boolean + visibleFilter?: boolean + openModalAuthen: () => void + openModalInfo: () => void + toggleFilter: () => void } - -const HeaderMenu = memo(({ isFull, isStickyHeader, visibleFilter, openModalAuthen, openModalInfo, toggleFilter }: Props) => { +const HeaderMenu = memo( + ({ + isFull, + isStickyHeader, + visibleFilter, + openModalAuthen, + openModalInfo, + toggleFilter, + }: Props) => { const router = useRouter() const { toggleCartDrawer } = useCartDrawer() - const optionMenu = useMemo(() => [ - { - onClick: openModalAuthen, - name: 'Login (Demo)', - }, - { - onClick: openModalInfo, - name: 'Create User Info (Demo)', - }, - { - link: '/account-not-login', - name: 'Account Not Login (Demo)', - }, - { - link: '/demo', - name: 'Notifications Empty (Demo)', - }, - { - link: ROUTE.NOTIFICATION, - name: 'Notifications', - }, - { - link: ROUTE.ACCOUNT, - name: 'Account', - }, - { - link: '/', - name: 'Logout', - }, + const { logout } = useLogout() - ], [openModalAuthen]) - return ( -
-
-
- -
- { - FILTER_PAGE.includes(router.pathname) && ( - - ) - } - -
- -
-
-
- -
-
- Search -
-
-
- -
+ const optionMenu = useMemo( + () => [ + { + onClick: openModalAuthen, + name: 'Login (Demo)', + }, + { + onClick: openModalInfo, + name: 'Create User Info (Demo)', + }, + { + link: '/account-not-login', + name: 'Account Not Login (Demo)', + }, + { + link: '/demo', + name: 'Notifications Empty (Demo)', + }, + { + link: ROUTE.NOTIFICATION, + name: 'Notifications', + }, + { + link: ROUTE.ACCOUNT, + name: 'Account', + }, + { + link: '/', + name: 'Logout', + onClick: logout, + }, + ], + [openModalAuthen, openModalInfo, logout] ) -}) + return ( +
+
+
+ +
+ {FILTER_PAGE.includes(router.pathname) && ( + + )} + +
+
+
+
+ +
+
+ Search +
+
+
+
    +
  • + + + + + +
  • +
  • + + + + + +
  • +
  • + + + +
  • +
  • + +
  • +
+
+ ) + } +) +HeaderMenu.displayName = 'HeaderMenu' export default HeaderMenu diff --git a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx index 590368da4..026710bbf 100644 --- a/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx +++ b/src/components/common/MenuNavigationProductList/MenuNavigationProductList.tsx @@ -24,7 +24,7 @@ const MenuNavigationProductList = ({categories,brands,featured,visible,onClose}: setDataSort({...dataSort,...value}); } function filter(){ - console.log(dataSort) + // console.log(dataSort) } return( <> diff --git a/src/components/hooks/auth/index.ts b/src/components/hooks/auth/index.ts new file mode 100644 index 000000000..494c974de --- /dev/null +++ b/src/components/hooks/auth/index.ts @@ -0,0 +1,3 @@ +export { default as useLogout } from './useLogout' + + diff --git a/src/components/hooks/auth/useLogout.tsx b/src/components/hooks/auth/useLogout.tsx new file mode 100644 index 000000000..5b1c095e1 --- /dev/null +++ b/src/components/hooks/auth/useLogout.tsx @@ -0,0 +1,34 @@ +import { LogoutMutation } from '@framework/schema' +import { logoutMutation } from '@framework/utils/mutations/log-out-mutation' +import { useState } from 'react' +import { CommonError } from 'src/domains/interfaces/CommonError' +import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils' +import rawFetcher from 'src/utils/rawFetcher' +import useActiveCustomer from '../useActiveCustomer' + +const useLogout = () => { + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + const { mutate } = useActiveCustomer() + + const logout = () => { + setError(null) + setLoading(true) + rawFetcher({ + query: logoutMutation, + }) + .then(({ data }) => { + if (!data.logout.success) { + throw CommonError.create('Logout fail') + } + localStorage.setItem(LOCAL_STORAGE_KEY.TOKEN, '') + mutate() + }) + .catch(setError) + .finally(() => setLoading(false)) + } + + return { loading, logout, error } +} + +export default useLogout diff --git a/src/components/hooks/useLogin.tsx b/src/components/hooks/useLogin.tsx index 5a000b9b1..e770ab7e0 100644 --- a/src/components/hooks/useLogin.tsx +++ b/src/components/hooks/useLogin.tsx @@ -4,6 +4,7 @@ import useActiveCustomer from './useActiveCustomer' import { CommonError } from 'src/domains/interfaces/CommonError' import rawFetcher from 'src/utils/rawFetcher' import { LoginMutation } from '@framework/schema' +import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils' const query = gql` mutation login($username: String!, $password: String!) { @@ -42,9 +43,8 @@ const useLogin = () => { throw CommonError.create(data.login.message, data.login.errorCode) } const authToken = headers.get('vendure-auth-token') - console.log("auth token: ", authToken) if (authToken != null) { - localStorage.setItem('token', authToken) + localStorage.setItem(LOCAL_STORAGE_KEY.TOKEN, authToken) return mutate() } }) diff --git a/src/components/modules/account/AccountPage/AccountPage.tsx b/src/components/modules/account/AccountPage/AccountPage.tsx index 47f5cf955..db5801235 100644 --- a/src/components/modules/account/AccountPage/AccountPage.tsx +++ b/src/components/modules/account/AccountPage/AccountPage.tsx @@ -87,7 +87,7 @@ const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => { const query = router.query[QUERY_KEY.TAB] as string const index = getTabIndex(query) setActiveTab(index) - }, [router.query[QUERY_KEY.TAB]]) + }, [router.query]) function showModal() { setModalVisible(true); diff --git a/src/components/modules/account/AccountPage/components/EditInfoModal/EditInfoModal.tsx b/src/components/modules/account/AccountPage/components/EditInfoModal/EditInfoModal.tsx index 4f1b8296b..06e6b2124 100644 --- a/src/components/modules/account/AccountPage/components/EditInfoModal/EditInfoModal.tsx +++ b/src/components/modules/account/AccountPage/components/EditInfoModal/EditInfoModal.tsx @@ -12,8 +12,6 @@ interface EditInfoModalProps { const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoModalProps) => { function saveInfo() { - console.log("saved !!!"); - closeModal(); } diff --git a/src/components/modules/home/HomeSubscribe/FormSubscribe/FormSubscribe.tsx b/src/components/modules/home/HomeSubscribe/FormSubscribe/FormSubscribe.tsx index c6b459a2a..ad30990f9 100644 --- a/src/components/modules/home/HomeSubscribe/FormSubscribe/FormSubscribe.tsx +++ b/src/components/modules/home/HomeSubscribe/FormSubscribe/FormSubscribe.tsx @@ -16,7 +16,6 @@ const FormSubscribe = () => { e.preventDefault && e.preventDefault() value = inputElementRef.current?.getValue()?.toString() || '' } - console.log("email here: ", value) } return ( diff --git a/src/utils/constanst.utils.ts b/src/utils/constanst.utils.ts index 9f9e0830f..f77991604 100644 --- a/src/utils/constanst.utils.ts +++ b/src/utils/constanst.utils.ts @@ -38,6 +38,10 @@ export const ACCOUNT_TAB = { FAVOURITE: 'wishlist', } +export const LOCAL_STORAGE_KEY = { + TOKEN: 'token' +} + export const QUERY_KEY = { TAB: 'tab', CATEGORY: 'category', diff --git a/src/utils/fetcher.ts b/src/utils/fetcher.ts index 53b736e5e..a86367398 100644 --- a/src/utils/fetcher.ts +++ b/src/utils/fetcher.ts @@ -1,5 +1,6 @@ import { request } from 'graphql-request' import { RequestDocument, Variables } from 'graphql-request/dist/types' +import { LOCAL_STORAGE_KEY } from './constanst.utils' interface QueryOptions { query: RequestDocument @@ -10,11 +11,7 @@ interface QueryOptions { const fetcher = async (options: QueryOptions): Promise => { const { query, variables } = options - // console.log('query') - // console.log(options) - const token = localStorage.getItem('token') - // console.log('token') - // console.log(token) + const token = localStorage.getItem(LOCAL_STORAGE_KEY.TOKEN) const res = await request( process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL as string, query, diff --git a/src/utils/rawFetcher.ts b/src/utils/rawFetcher.ts index 8ee6a05ee..c26b2ab20 100644 --- a/src/utils/rawFetcher.ts +++ b/src/utils/rawFetcher.ts @@ -1,5 +1,6 @@ import { rawRequest } from 'graphql-request' import { RequestDocument, Variables } from 'graphql-request/dist/types' +import { LOCAL_STORAGE_KEY } from './constanst.utils' interface QueryOptions { query: RequestDocument @@ -14,10 +15,12 @@ const rawFetcher = ({ onLoad = () => true, }: QueryOptions): Promise<{ data: T; headers: any }> => { onLoad(true) + const token = localStorage.getItem(LOCAL_STORAGE_KEY.TOKEN) return rawRequest( process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL as string, query as string, - variables + variables, + token ? { Authorization: 'Bearer ' + token } : {} ) .then(({ data, headers }) => { return { data, headers } From 48ab2f224297b7a3cdf0ac1bd3d40453c2e2a1f0 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 29 Sep 2021 17:11:58 +0700 Subject: [PATCH 11/16] :sparkles: feat: show menu in mode login/ nit login :%s --- pages/account-not-login.tsx | 15 -------- src/components/common/Header/Header.tsx | 28 ++++++++++----- .../components/HeaderMenu/HeaderMenu.tsx | 36 ++++++++++++------- 3 files changed, 43 insertions(+), 36 deletions(-) delete mode 100644 pages/account-not-login.tsx diff --git a/pages/account-not-login.tsx b/pages/account-not-login.tsx deleted file mode 100644 index cbda16486..000000000 --- a/pages/account-not-login.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { Layout } from 'src/components/common'; -import { AccountSignIn } from 'src/components/modules/account'; - -const AccountNotLogin = () => { - return ( - <> - - - ); -}; - -AccountNotLogin.Layout = Layout - -export default AccountNotLogin; \ No newline at end of file diff --git a/src/components/common/Header/Header.tsx b/src/components/common/Header/Header.tsx index a0b220c08..f3a514970 100644 --- a/src/components/common/Header/Header.tsx +++ b/src/components/common/Header/Header.tsx @@ -1,7 +1,6 @@ import classNames from 'classnames' -import React, { memo, useEffect, useMemo, useRef, useState } from 'react' +import React, { memo, useEffect, useRef, useState } from 'react' import { useModalCommon } from 'src/components/hooks' -import { CartDrawer } from '..' import ModalAuthenticate from '../ModalAuthenticate/ModalAuthenticate' import ModalCreateUserInfo from '../ModalCreateUserInfo/ModalCreateUserInfo' import HeaderHighLight from './components/HeaderHighLight/HeaderHighLight' @@ -17,6 +16,7 @@ interface props { const Header = memo(({ toggleFilter, visibleFilter }: props) => { const headeFullRef = useRef(null) const [isFullHeader, setIsFullHeader] = useState(true) + const [isModeAuthenRegister, setIsModeAuthenRegister] = useState(false) const { visible: visibleModalAuthen, closeModal: closeModalAuthen, openModal: openModalAuthen } = useModalCommon({ initialValue: false }) const { visible: visibleModalInfo, closeModal: closeModalInfo, openModal: openModalInfo } = useModalCommon({ initialValue: false }) @@ -32,7 +32,17 @@ const Header = memo(({ toggleFilter, visibleFilter }: props) => { return () => { window.removeEventListener('scroll', handleScroll) } - }, [headeFullRef.current]) + }, []) + + const openModalRegister = () => { + setIsModeAuthenRegister(true) + openModalAuthen() + } + + const openModalLogin = () => { + setIsModeAuthenRegister(false) + openModalAuthen() + } return ( <> @@ -43,7 +53,8 @@ const Header = memo(({ toggleFilter, visibleFilter }: props) => {
@@ -54,16 +65,17 @@ const Header = memo(({ toggleFilter, visibleFilter }: props) => { isFull={isFullHeader} visibleFilter={visibleFilter} toggleFilter={toggleFilter} - openModalAuthen={openModalAuthen} - openModalInfo={openModalInfo} /> + openModalLogin={openModalLogin} + openModalRegister = {openModalRegister} + openModalInfo={openModalInfo} + /> - + - ) }) diff --git a/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx b/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx index c29890dbd..a4977b198 100644 --- a/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx +++ b/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx @@ -22,12 +22,14 @@ import { import Logo from '../../../Logo/Logo' import s from './HeaderMenu.module.scss' import { useLogout } from '../../../../hooks/auth' +import useActiveCustomer from 'src/components/hooks/useActiveCustomer' interface Props { children?: any isFull?: boolean isStickyHeader?: boolean visibleFilter?: boolean - openModalAuthen: () => void + openModalLogin: () => void + openModalRegister: () => void openModalInfo: () => void toggleFilter: () => void } @@ -37,29 +39,37 @@ const HeaderMenu = memo( isFull, isStickyHeader, visibleFilter, - openModalAuthen, + openModalLogin, + openModalRegister, openModalInfo, toggleFilter, }: Props) => { const router = useRouter() const { toggleCartDrawer } = useCartDrawer() + const { customer } = useActiveCustomer() const { logout } = useLogout() - const optionMenu = useMemo( + const optionMenuNotAuthen = useMemo( () => [ { - onClick: openModalAuthen, - name: 'Login (Demo)', + onClick: openModalLogin, + name: 'Sign in', }, { - onClick: openModalInfo, - name: 'Create User Info (Demo)', - }, - { - link: '/account-not-login', - name: 'Account Not Login (Demo)', + onClick: openModalRegister, + name: 'Create account', }, + ], + [openModalLogin, openModalRegister] + ) + + const optionMenu = useMemo( + () => [ + // { + // onClick: openModalInfo, + // name: 'Create User Info (Demo)', + // }, { link: '/demo', name: 'Notifications Empty (Demo)', @@ -78,7 +88,7 @@ const HeaderMenu = memo( onClick: logout, }, ], - [openModalAuthen, openModalInfo, logout] + [logout] ) return (
  • - +
  • From 1383d668329817734c7b47809864ab1d6c44a227 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 29 Sep 2021 17:24:45 +0700 Subject: [PATCH 12/16] :recycle: enhan: auto focus input email :%s --- .../InputFiledInForm/InputFiledInForm.tsx | 19 +++++++++++++++---- .../components/FormRegister/FormRegister.tsx | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/components/common/InputFiledInForm/InputFiledInForm.tsx b/src/components/common/InputFiledInForm/InputFiledInForm.tsx index 4b848e244..807aea561 100644 --- a/src/components/common/InputFiledInForm/InputFiledInForm.tsx +++ b/src/components/common/InputFiledInForm/InputFiledInForm.tsx @@ -1,10 +1,13 @@ import classNames from 'classnames' import { Field } from 'formik' -import React, { useMemo, useRef } from 'react' +import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react' import { IconCheck, IconError } from 'src/components/icons' import { KEY } from 'src/utils/constanst.utils' import s from './InputFiledInForm.module.scss' +type Ref = { + focus: () => void +} | null interface Props { placeholder?: string type?: 'text' | 'number' | 'email' | 'password' @@ -21,7 +24,7 @@ interface Props { onEnter?: (value: string | number) => void } -const InputFiledInForm = ({ +const InputFiledInForm = forwardRef(({ name, placeholder, type, @@ -32,9 +35,15 @@ const InputFiledInForm = ({ isShowIconSuccess, error, onEnter, -}: Props) => { +}: Props, ref) => { const inputElementRef = useRef(null) + useImperativeHandle(ref, () => ({ + focus: () => { + inputElementRef.current?.focus() + }, + })) + const iconElement = useMemo(() => { if (error) { return ( @@ -83,12 +92,14 @@ const InputFiledInForm = ({ placeholder={placeholder} onKeyDown={handleKeyDown} type={type} + innerRef={inputElementRef} /> + {error &&
    {error}
    } ) -} +}) InputFiledInForm.displayName = 'InputFiledInForm' export default InputFiledInForm diff --git a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx index 1cccf6977..9dd1f01ff 100644 --- a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx +++ b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx @@ -83,6 +83,7 @@ const FormRegister = ({ onSwitch, isHide }: Props) => { Date: Wed, 29 Sep 2021 17:56:41 +0700 Subject: [PATCH 13/16] :sparkles: feat: login :%s --- pages/test.tsx | 4 +- .../InputFiledInForm/InputFiledInForm.tsx | 12 +- .../common/MessageCommon/MessageCommon.tsx | 1 + .../MessageItem/MessageItem.module.scss | 3 +- .../MessageCommon/MessageItem/MessageItem.tsx | 2 +- .../components/FormAuthen.module.scss | 2 +- .../components/FormLogin/FormLogin.tsx | 113 ++++++++++++------ .../components/FormRegister/FormRegister.tsx | 8 +- src/components/hooks/useLogin.tsx | 15 ++- src/utils/errrorMapping.ts | 14 +++ 10 files changed, 114 insertions(+), 60 deletions(-) create mode 100644 src/utils/errrorMapping.ts diff --git a/pages/test.tsx b/pages/test.tsx index a140d5aee..b60fe63c7 100644 --- a/pages/test.tsx +++ b/pages/test.tsx @@ -2,10 +2,10 @@ import { Layout } from 'src/components/common' import { useMessage } from 'src/components/contexts' export default function Test() { - const { showMessageSuccess } = useMessage() + const { showMessageError } = useMessage() const handleClick = () => { - showMessageSuccess("Create account successfully") + showMessageError("Create account successfully") } return ( diff --git a/src/components/common/InputFiledInForm/InputFiledInForm.tsx b/src/components/common/InputFiledInForm/InputFiledInForm.tsx index 807aea561..22389b78d 100644 --- a/src/components/common/InputFiledInForm/InputFiledInForm.tsx +++ b/src/components/common/InputFiledInForm/InputFiledInForm.tsx @@ -63,10 +63,14 @@ const InputFiledInForm = forwardRef(({ return <> }, [icon, error, isShowIconSuccess]) - const handleKeyDown = (e: any) => { - if (e.key === KEY.ENTER && onEnter) { - const value = inputElementRef.current?.value || '' - onEnter(value) + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === KEY.ENTER) { + e.stopPropagation() + e.preventDefault() + if (onEnter) { + const value = inputElementRef.current?.value || '' + onEnter(value) + } } } diff --git a/src/components/common/MessageCommon/MessageCommon.tsx b/src/components/common/MessageCommon/MessageCommon.tsx index f53469c50..f15f8f443 100644 --- a/src/components/common/MessageCommon/MessageCommon.tsx +++ b/src/components/common/MessageCommon/MessageCommon.tsx @@ -14,6 +14,7 @@ const MessageCommon = memo(({ messages, onRemove }: Props) => { diff --git a/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss b/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss index f2583810d..bc0ee8e46 100644 --- a/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss +++ b/src/components/common/MessageCommon/MessageItem/MessageItem.module.scss @@ -4,12 +4,11 @@ @apply shadow-sm flex justify-center items-center cursor-default; width: fit-content; padding: 0.8rem 2.4rem; - margin-bottom: .8rem; + margin: 0 auto .8rem; border-radius: 0.8rem; transition: all .5s; animation: showMessage .5s; width: max-content; - margin: 0 1.6rem; &.hide { display: none; diff --git a/src/components/common/MessageCommon/MessageItem/MessageItem.tsx b/src/components/common/MessageCommon/MessageItem/MessageItem.tsx index 89f9b6600..6ed95c1a3 100644 --- a/src/components/common/MessageCommon/MessageItem/MessageItem.tsx +++ b/src/components/common/MessageCommon/MessageItem/MessageItem.tsx @@ -40,7 +40,7 @@ const MessageItem = memo( useEffect(() => { if (isHide && !isMouseOver && onRemove) { - onRemove(id || 0) + // onRemove(id || 0) } }, [isHide, isMouseOver, onRemove, id]) diff --git a/src/components/common/ModalAuthenticate/components/FormAuthen.module.scss b/src/components/common/ModalAuthenticate/components/FormAuthen.module.scss index 2ec8bf91f..eb28ba509 100644 --- a/src/components/common/ModalAuthenticate/components/FormAuthen.module.scss +++ b/src/components/common/ModalAuthenticate/components/FormAuthen.module.scss @@ -1,7 +1,7 @@ @import '../../../../styles/utilities'; .formAuthen { - @apply bg-white w-full u-form; + @apply bg-white w-full; .inner { @screen md { width: 60rem; diff --git a/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx b/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx index 9dd5296f8..5ec394541 100644 --- a/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx +++ b/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx @@ -1,9 +1,13 @@ +import { Form, Formik } from 'formik' import Link from 'next/link' import React, { useEffect, useRef, useState } from 'react' -import { ButtonCommon, Inputcommon, InputPassword } from 'src/components/common' -import { ROUTE } from 'src/utils/constanst.utils' -import { CustomInputCommon } from 'src/utils/type.utils' +import { ButtonCommon, Inputcommon, InputFiledInForm, InputPassword, InputPasswordFiledInForm } from 'src/components/common' +import { useMessage } from 'src/components/contexts' import useLogin from 'src/components/hooks/useLogin' +import { ROUTE } from 'src/utils/constanst.utils' +import { LANGUAGE } from 'src/utils/language.utils' +import { CustomInputCommon } from 'src/utils/type.utils' +import * as Yup from 'yup' import s from '../FormAuthen.module.scss' import SocialAuthen from '../SocialAuthen/SocialAuthen' import styles from './FormLogin.module.scss' @@ -13,15 +17,17 @@ interface Props { onSwitch: () => void } +const DisplayingErrorMessagesSchema = Yup.object().shape({ + email: Yup.string().email('Your email was wrong').required('Required'), + password: Yup.string() + .max(30, 'Password is too long') + .required('Required'), +}) + const FormLogin = ({ onSwitch, isHide }: Props) => { const emailRef = useRef(null) - const { loading, login, error } = useLogin() - const [email, setEmail] = useState('') - const [password, setPassword] = useState('') - - const onLogin = () => { - login({ username: email, password }) - } + const { loading, login } = useLogin() + const { showMessageSuccess, showMessageError } = useMessage() useEffect(() => { if (!isHide) { @@ -29,42 +35,71 @@ const FormLogin = ({ onSwitch, isHide }: Props) => { } }, [isHide]) - useEffect(() => { - if (error) { - alert(error.message) + const onLogin = (values: { email: string; password: string }) => { + login({ username: values.email, password: values.password }, onLoginCallBack) + } + + const onLoginCallBack = (isSuccess: boolean, message?: string) => { + if (isSuccess) { + showMessageSuccess("Login successfully!", 4000) + } else { + showMessageError(message || LANGUAGE.MESSAGE.ERROR) } - }, [error]) + } return (
    - setEmail(val.toString())} - type="email" - ref={emailRef} - /> + + {({ errors, touched, isValid, submitForm }) => ( +
    +
    + + +
    +
    + + + Forgot Password? + + + + Sign in + +
    +
    + )} +
    +
    - {/* */} - setPassword(val.toString())} - /> -
    -
    - - - Forgot Password? - - - - Sign in - -
    Don't have an account? diff --git a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx index 9dd1f01ff..7f63ef4e0 100644 --- a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx +++ b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx @@ -33,7 +33,7 @@ const DisplayingErrorMessagesSchema = Yup.object().shape({ const FormRegister = ({ onSwitch, isHide }: Props) => { const emailRef = useRef(null) - const { loading, signup, error } = useSignup() + const { loading, signup } = useSignup() const { showMessageSuccess, showMessageError } = useMessage() useEffect(() => { @@ -54,12 +54,6 @@ const FormRegister = ({ onSwitch, isHide }: Props) => { } } - useEffect(() => { - if (error) { - alert(error.message) - } - }, [error]) - return (
    { const [error, setError] = useState(null) const { mutate } = useActiveCustomer() - const login = (options: LoginInput) => { + const login = (options: LoginInput, + fCallBack: (isSuccess: boolean, message?: string) => void + ) => { setError(null) setLoading(true) rawFetcher({ @@ -40,15 +43,19 @@ const useLogin = () => { }) .then(({ data, headers }) => { if (data.login.__typename !== 'CurrentUser') { - throw CommonError.create(data.login.message, data.login.errorCode) + throw CommonError.create(errorMapping(data.login.message), data.login.errorCode) } const authToken = headers.get('vendure-auth-token') if (authToken != null) { localStorage.setItem(LOCAL_STORAGE_KEY.TOKEN, authToken) - return mutate() + mutate() } + fCallBack(true) + }) + .catch((error) => { + setError(error) + fCallBack(false, error.message) }) - .catch(setError) .finally(() => setLoading(false)) } diff --git a/src/utils/errrorMapping.ts b/src/utils/errrorMapping.ts new file mode 100644 index 000000000..f216e1d57 --- /dev/null +++ b/src/utils/errrorMapping.ts @@ -0,0 +1,14 @@ +import { LANGUAGE } from "./language.utils"; + +export function errorMapping(message?: string) { + if (!message) { + return LANGUAGE.MESSAGE.ERROR + } + + switch (message) { + case 'The provided credentials are invalid': + return 'The email address or password is incorrect!' + default: + return LANGUAGE.MESSAGE.ERROR + } +} \ No newline at end of file From 6c28dc3d7d0f555cb1ac5c645e0c11e3da58edd0 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Wed, 29 Sep 2021 18:00:14 +0700 Subject: [PATCH 14/16] :bug: bug: remove message item when hide :%s --- .../common/MessageCommon/MessageItem/MessageItem.tsx | 2 +- src/components/common/ModalAuthenticate/ModalAuthenticate.tsx | 2 +- .../ModalAuthenticate/components/FormLogin/FormLogin.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/common/MessageCommon/MessageItem/MessageItem.tsx b/src/components/common/MessageCommon/MessageItem/MessageItem.tsx index 6ed95c1a3..89f9b6600 100644 --- a/src/components/common/MessageCommon/MessageItem/MessageItem.tsx +++ b/src/components/common/MessageCommon/MessageItem/MessageItem.tsx @@ -40,7 +40,7 @@ const MessageItem = memo( useEffect(() => { if (isHide && !isMouseOver && onRemove) { - // onRemove(id || 0) + onRemove(id || 0) } }, [isHide, isMouseOver, onRemove, id]) diff --git a/src/components/common/ModalAuthenticate/ModalAuthenticate.tsx b/src/components/common/ModalAuthenticate/ModalAuthenticate.tsx index b086098cc..08cbc96e6 100644 --- a/src/components/common/ModalAuthenticate/ModalAuthenticate.tsx +++ b/src/components/common/ModalAuthenticate/ModalAuthenticate.tsx @@ -32,7 +32,7 @@ const ModalAuthenticate = ({ visible, mode, closeModal }: Props) => { closeModal() router.push(ROUTE.ACCOUNT) } - }, [customer, visible]) + }, [customer, visible, closeModal, router]) const onSwitch = () => { setIsLogin(!isLogin) diff --git a/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx b/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx index 5ec394541..713b78629 100644 --- a/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx +++ b/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx @@ -1,7 +1,7 @@ import { Form, Formik } from 'formik' import Link from 'next/link' -import React, { useEffect, useRef, useState } from 'react' -import { ButtonCommon, Inputcommon, InputFiledInForm, InputPassword, InputPasswordFiledInForm } from 'src/components/common' +import React, { useEffect, useRef } from 'react' +import { ButtonCommon, InputFiledInForm, InputPasswordFiledInForm } from 'src/components/common' import { useMessage } from 'src/components/contexts' import useLogin from 'src/components/hooks/useLogin' import { ROUTE } from 'src/utils/constanst.utils' From 4a9c36bf0a849ebad1da910b2062cac6cd54c320 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Thu, 30 Sep 2021 09:27:01 +0700 Subject: [PATCH 15/16] refactor folder hooks/auth --- pages/account.tsx | 2 +- .../common/Header/components/HeaderMenu/HeaderMenu.tsx | 2 +- .../common/ModalAuthenticate/ModalAuthenticate.tsx | 2 +- .../ModalAuthenticate/components/FormLogin/FormLogin.tsx | 2 +- .../components/FormRegister/FormRegister.tsx | 2 +- src/components/hooks/auth/index.ts | 5 ++++- src/components/hooks/{ => auth}/useActiveCustomer.tsx | 0 src/components/hooks/{ => auth}/useLogin.tsx | 0 src/components/hooks/auth/useLogout.tsx | 2 +- src/components/hooks/{ => auth}/useSignup.tsx | 0 src/components/hooks/{ => auth}/useVerifyCustomer.tsx | 2 +- src/components/hooks/index.ts | 2 -- .../components/AccountInfomation/AccountInfomation.tsx | 2 +- 13 files changed, 12 insertions(+), 11 deletions(-) rename src/components/hooks/{ => auth}/useActiveCustomer.tsx (100%) rename src/components/hooks/{ => auth}/useLogin.tsx (100%) rename src/components/hooks/{ => auth}/useSignup.tsx (100%) rename src/components/hooks/{ => auth}/useVerifyCustomer.tsx (95%) diff --git a/pages/account.tsx b/pages/account.tsx index df4121aa7..690e72cb3 100644 --- a/pages/account.tsx +++ b/pages/account.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Layout } from 'src/components/common' -import useActiveCustomer from 'src/components/hooks/useActiveCustomer' +import { useActiveCustomer } from 'src/components/hooks/auth' import { AccountPage, AccountSignIn } from 'src/components/modules/account' const Account = () => { diff --git a/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx b/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx index a4977b198..4f7e5d21a 100644 --- a/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx +++ b/src/components/common/Header/components/HeaderMenu/HeaderMenu.tsx @@ -22,7 +22,7 @@ import { import Logo from '../../../Logo/Logo' import s from './HeaderMenu.module.scss' import { useLogout } from '../../../../hooks/auth' -import useActiveCustomer from 'src/components/hooks/useActiveCustomer' +import { useActiveCustomer } from 'src/components/hooks/auth' interface Props { children?: any isFull?: boolean diff --git a/src/components/common/ModalAuthenticate/ModalAuthenticate.tsx b/src/components/common/ModalAuthenticate/ModalAuthenticate.tsx index 08cbc96e6..145155268 100644 --- a/src/components/common/ModalAuthenticate/ModalAuthenticate.tsx +++ b/src/components/common/ModalAuthenticate/ModalAuthenticate.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames' import { useRouter } from 'next/router' import React, { useEffect, useState } from 'react' -import useActiveCustomer from 'src/components/hooks/useActiveCustomer' +import { useActiveCustomer } from 'src/components/hooks/auth' import { ROUTE } from 'src/utils/constanst.utils' import ModalCommon from '../ModalCommon/ModalCommon' import FormLogin from './components/FormLogin/FormLogin' diff --git a/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx b/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx index 713b78629..279137a84 100644 --- a/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx +++ b/src/components/common/ModalAuthenticate/components/FormLogin/FormLogin.tsx @@ -3,7 +3,7 @@ import Link from 'next/link' import React, { useEffect, useRef } from 'react' import { ButtonCommon, InputFiledInForm, InputPasswordFiledInForm } from 'src/components/common' import { useMessage } from 'src/components/contexts' -import useLogin from 'src/components/hooks/useLogin' +import useLogin from 'src/components/hooks/auth/useLogin' import { ROUTE } from 'src/utils/constanst.utils' import { LANGUAGE } from 'src/utils/language.utils' import { CustomInputCommon } from 'src/utils/type.utils' diff --git a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx index 7f63ef4e0..c08b9120f 100644 --- a/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx +++ b/src/components/common/ModalAuthenticate/components/FormRegister/FormRegister.tsx @@ -10,7 +10,7 @@ import { useMessage } from 'src/components/contexts' import { LANGUAGE } from 'src/utils/language.utils' import { CustomInputCommon } from 'src/utils/type.utils' import * as Yup from 'yup' -import { useSignup } from '../../../../hooks' +import { useSignup } from '../../../../hooks/auth' import s from '../FormAuthen.module.scss' import SocialAuthen from '../SocialAuthen/SocialAuthen' import styles from './FormRegister.module.scss' diff --git a/src/components/hooks/auth/index.ts b/src/components/hooks/auth/index.ts index 494c974de..845617bcd 100644 --- a/src/components/hooks/auth/index.ts +++ b/src/components/hooks/auth/index.ts @@ -1,3 +1,6 @@ +export { default as useSignup } from './useSignup' +export { default as useLogin } from './useLogin' export { default as useLogout } from './useLogout' - +export { default as useVerifyCustomer } from './useVerifyCustomer' +export { default as useActiveCustomer } from './useActiveCustomer' diff --git a/src/components/hooks/useActiveCustomer.tsx b/src/components/hooks/auth/useActiveCustomer.tsx similarity index 100% rename from src/components/hooks/useActiveCustomer.tsx rename to src/components/hooks/auth/useActiveCustomer.tsx diff --git a/src/components/hooks/useLogin.tsx b/src/components/hooks/auth/useLogin.tsx similarity index 100% rename from src/components/hooks/useLogin.tsx rename to src/components/hooks/auth/useLogin.tsx diff --git a/src/components/hooks/auth/useLogout.tsx b/src/components/hooks/auth/useLogout.tsx index 5b1c095e1..93398bf49 100644 --- a/src/components/hooks/auth/useLogout.tsx +++ b/src/components/hooks/auth/useLogout.tsx @@ -4,7 +4,7 @@ import { useState } from 'react' import { CommonError } from 'src/domains/interfaces/CommonError' import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils' import rawFetcher from 'src/utils/rawFetcher' -import useActiveCustomer from '../useActiveCustomer' +import useActiveCustomer from './useActiveCustomer' const useLogout = () => { const [loading, setLoading] = useState(false) diff --git a/src/components/hooks/useSignup.tsx b/src/components/hooks/auth/useSignup.tsx similarity index 100% rename from src/components/hooks/useSignup.tsx rename to src/components/hooks/auth/useSignup.tsx diff --git a/src/components/hooks/useVerifyCustomer.tsx b/src/components/hooks/auth/useVerifyCustomer.tsx similarity index 95% rename from src/components/hooks/useVerifyCustomer.tsx rename to src/components/hooks/auth/useVerifyCustomer.tsx index d86a5b8ed..af05729a7 100644 --- a/src/components/hooks/useVerifyCustomer.tsx +++ b/src/components/hooks/auth/useVerifyCustomer.tsx @@ -2,7 +2,7 @@ import { VerifyCustomerAccountMutation } from '@framework/schema' import { useState } from 'react' import { CommonError } from 'src/domains/interfaces/CommonError' import rawFetcher from 'src/utils/rawFetcher' -import { VERIFY_CUSTOMER_ACCOUNT } from '../../graphql/mutation' +import { VERIFY_CUSTOMER_ACCOUNT } from '../../../graphql/mutation' import useActiveCustomer from './useActiveCustomer' interface VerifyInput { diff --git a/src/components/hooks/index.ts b/src/components/hooks/index.ts index 4a25fd98e..cf83feb42 100644 --- a/src/components/hooks/index.ts +++ b/src/components/hooks/index.ts @@ -1,3 +1 @@ export { default as useModalCommon } from './useModalCommon' -export { default as useVerifyCustomer } from './useVerifyCustomer' -export { default as useSignup } from './useSignup' diff --git a/src/components/modules/account/AccountPage/components/AccountInfomation/AccountInfomation.tsx b/src/components/modules/account/AccountPage/components/AccountInfomation/AccountInfomation.tsx index 5da39b54d..b025d5744 100644 --- a/src/components/modules/account/AccountPage/components/AccountInfomation/AccountInfomation.tsx +++ b/src/components/modules/account/AccountPage/components/AccountInfomation/AccountInfomation.tsx @@ -5,7 +5,7 @@ import Image from 'next/image' import avatar from '../../assets/avatar.png' import { ButtonCommon } from 'src/components/common' -import useActiveCustomer from 'src/components/hooks/useActiveCustomer' +import { useActiveCustomer } from 'src/components/hooks/auth' interface AccountProps { name: string From b752b0f7a4d65586f528001429977ea217bae6a6 Mon Sep 17 00:00:00 2001 From: lytrankieio123 Date: Thu, 30 Sep 2021 09:45:57 +0700 Subject: [PATCH 16/16] :hammer: refactor: using query and mutation from framework vendure :%s --- .../verify-customer-account-mutation.ts | 9 ++------- .../hooks/auth/useActiveCustomer.tsx | 15 ++------------- src/components/hooks/auth/useLogin.tsx | 19 ++----------------- src/components/hooks/auth/useSignup.tsx | 19 ++----------------- .../hooks/auth/useVerifyCustomer.tsx | 4 ++-- .../VerifyCustomerAccount.tsx | 3 ++- src/graphql/mutation/index.ts | 1 - src/graphql/query/index.ts | 1 - src/graphql/query/user.query.ts | 1 - 9 files changed, 12 insertions(+), 60 deletions(-) rename src/graphql/mutation/user.mutation.ts => framework/vendure/utils/mutations/verify-customer-account-mutation.ts (59%) delete mode 100644 src/graphql/mutation/index.ts delete mode 100644 src/graphql/query/index.ts delete mode 100644 src/graphql/query/user.query.ts diff --git a/src/graphql/mutation/user.mutation.ts b/framework/vendure/utils/mutations/verify-customer-account-mutation.ts similarity index 59% rename from src/graphql/mutation/user.mutation.ts rename to framework/vendure/utils/mutations/verify-customer-account-mutation.ts index 40812db19..2df2bc53d 100644 --- a/src/graphql/mutation/user.mutation.ts +++ b/framework/vendure/utils/mutations/verify-customer-account-mutation.ts @@ -1,8 +1,5 @@ -import { gql } from 'graphql-request' - - -export const VERIFY_CUSTOMER_ACCOUNT = gql` -mutation verifyCustomerAccount($token: String!, $password: String) { +export const verifyCustomerAccountMutaton = /* GraphQL */ ` + mutation verifyCustomerAccount($token: String!, $password: String) { verifyCustomerAccount( token: $token, password: $password) { __typename ...on CurrentUser { @@ -16,5 +13,3 @@ mutation verifyCustomerAccount($token: String!, $password: String) { } } ` - - diff --git a/src/components/hooks/auth/useActiveCustomer.tsx b/src/components/hooks/auth/useActiveCustomer.tsx index 4226a15b7..f0f4f6fef 100644 --- a/src/components/hooks/auth/useActiveCustomer.tsx +++ b/src/components/hooks/auth/useActiveCustomer.tsx @@ -1,21 +1,10 @@ import { ActiveCustomerQuery } from '@framework/schema' -import { gql } from 'graphql-request' +import { activeCustomerQuery } from '@framework/utils/queries/active-customer-query' import gglFetcher from 'src/utils/gglFetcher' import useSWR from 'swr' -const query = gql` - query activeCustomer { - activeCustomer { - id - firstName - lastName - emailAddress - } - } -` - const useActiveCustomer = () => { - const { data, ...rest } = useSWR([query], gglFetcher) + const { data, ...rest } = useSWR([activeCustomerQuery], gglFetcher) return { customer: data?.activeCustomer, ...rest } } diff --git a/src/components/hooks/auth/useLogin.tsx b/src/components/hooks/auth/useLogin.tsx index 0b97e8d8b..d38390004 100644 --- a/src/components/hooks/auth/useLogin.tsx +++ b/src/components/hooks/auth/useLogin.tsx @@ -1,4 +1,3 @@ -import { gql } from 'graphql-request' import { useState } from 'react' import useActiveCustomer from './useActiveCustomer' import { CommonError } from 'src/domains/interfaces/CommonError' @@ -6,21 +5,7 @@ import rawFetcher from 'src/utils/rawFetcher' import { LoginMutation } from '@framework/schema' import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils' import { errorMapping } from 'src/utils/errrorMapping' - -const query = gql` - mutation login($username: String!, $password: String!) { - login(username: $username, password: $password) { - __typename - ... on CurrentUser { - id - } - ... on ErrorResult { - errorCode - message - } - } - } -` +import { loginMutation } from '@framework/utils/mutations/log-in-mutation' interface LoginInput { username: string @@ -38,7 +23,7 @@ const useLogin = () => { setError(null) setLoading(true) rawFetcher({ - query, + query: loginMutation, variables: options, }) .then(({ data, headers }) => { diff --git a/src/components/hooks/auth/useSignup.tsx b/src/components/hooks/auth/useSignup.tsx index 4842915e1..922460c77 100644 --- a/src/components/hooks/auth/useSignup.tsx +++ b/src/components/hooks/auth/useSignup.tsx @@ -1,24 +1,9 @@ -import { gql } from 'graphql-request' import { useState } from 'react' import useActiveCustomer from './useActiveCustomer' import { SignupMutation } from '@framework/schema' import fetcher from 'src/utils/fetcher' import { CommonError } from 'src/domains/interfaces/CommonError' - -const query = gql` - mutation signup($input: RegisterCustomerInput!) { - registerCustomerAccount(input: $input) { - __typename - ... on Success { - success - } - ... on ErrorResult { - errorCode - message - } - } - } -` +import { signupMutation } from '@framework/utils/mutations/sign-up-mutation' interface SignupInput { email: string @@ -39,7 +24,7 @@ const useSignup = () => { setError(null) setLoading(true) fetcher({ - query, + query: signupMutation, variables: { input: { firstName, diff --git a/src/components/hooks/auth/useVerifyCustomer.tsx b/src/components/hooks/auth/useVerifyCustomer.tsx index af05729a7..181444b51 100644 --- a/src/components/hooks/auth/useVerifyCustomer.tsx +++ b/src/components/hooks/auth/useVerifyCustomer.tsx @@ -2,8 +2,8 @@ import { VerifyCustomerAccountMutation } from '@framework/schema' import { useState } from 'react' import { CommonError } from 'src/domains/interfaces/CommonError' import rawFetcher from 'src/utils/rawFetcher' -import { VERIFY_CUSTOMER_ACCOUNT } from '../../../graphql/mutation' import useActiveCustomer from './useActiveCustomer' +import { verifyCustomerAccountMutaton } from '@framework/utils/mutations/verify-customer-account-mutation' interface VerifyInput { token: string @@ -22,7 +22,7 @@ const useVerifyCustomer = () => { setError(null) setLoading(true) rawFetcher({ - query: VERIFY_CUSTOMER_ACCOUNT, + query: verifyCustomerAccountMutaton, variables: options, }) .then(({ data }) => { diff --git a/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx b/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx index e2c93db2e..d03bd6aa4 100644 --- a/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx +++ b/src/components/modules/verify-customer/VerifyCustomerAccount/VerifyCustomerAccount.tsx @@ -2,7 +2,8 @@ import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import { ButtonCommon } from 'src/components/common' import LoadingCommon from 'src/components/common/LoadingCommon/LoadingCommon' -import { useModalCommon, useVerifyCustomer } from 'src/components/hooks' +import { useModalCommon } from 'src/components/hooks' +import { useVerifyCustomer } from 'src/components/hooks/auth' import { ROUTE } from 'src/utils/constanst.utils' import s from './VerifyCustomerAccount.module.scss' import Link from 'next/link' diff --git a/src/graphql/mutation/index.ts b/src/graphql/mutation/index.ts deleted file mode 100644 index 7ea0d4f82..000000000 --- a/src/graphql/mutation/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user.mutation' diff --git a/src/graphql/query/index.ts b/src/graphql/query/index.ts deleted file mode 100644 index 47c53bc6d..000000000 --- a/src/graphql/query/index.ts +++ /dev/null @@ -1 +0,0 @@ -// export * from './user.mutation' diff --git a/src/graphql/query/user.query.ts b/src/graphql/query/user.query.ts deleted file mode 100644 index e9e7409b4..000000000 --- a/src/graphql/query/user.query.ts +++ /dev/null @@ -1 +0,0 @@ -// query here \ No newline at end of file