feat: get-edit-user-info

This commit is contained in:
Quangnhankie
2021-10-06 12:10:04 +07:00
parent a2cec5788e
commit dcfa1a1ece
17 changed files with 380 additions and 96 deletions

View File

@@ -1,4 +1,4 @@
import { FacetValue } from './schema.d'; import { FacetValue, UpdateAddressInput } from './schema.d';
export type Maybe<T> = T | null export type Maybe<T> = T | null
export type Exact<T extends { [key: string]: unknown }> = { export type Exact<T extends { [key: string]: unknown }> = {
[K in keyof T]: T[K] [K in keyof T]: T[K]
@@ -304,6 +304,11 @@ export type MutationResetPasswordArgs = {
} }
export type Address = Node & { export type Address = Node & {
updateCustomerAddress:
| {
__typename?: 'Address'
id: Scalars['ID']
}
__typename?: 'Address' __typename?: 'Address'
id: Scalars['ID'] id: Scalars['ID']
createdAt: Scalars['DateTime'] createdAt: Scalars['DateTime']
@@ -1462,6 +1467,11 @@ export type CustomerListOptions = {
} }
export type Customer = Node & { export type Customer = Node & {
updateCustomer:
| {
__typename?: 'Customer'
id: Scalars['ID']
}
__typename?: 'Customer' __typename?: 'Customer'
id: Scalars['ID'] id: Scalars['ID']
createdAt: Scalars['DateTime'] createdAt: Scalars['DateTime']
@@ -1469,7 +1479,7 @@ export type Customer = Node & {
title?: Maybe<Scalars['String']> title?: Maybe<Scalars['String']>
firstName: Scalars['String'] firstName: Scalars['String']
lastName: Scalars['String'] lastName: Scalars['String']
phoneNumber?: Maybe<Scalars['String']> phoneNumber?: Maybe<Scalars['String']>
emailAddress: Scalars['String'] emailAddress: Scalars['String']
addresses?: Maybe<Array<Address>> addresses?: Maybe<Array<Address>>
orders: OrderList orders: OrderList
@@ -3201,7 +3211,7 @@ export type ActiveCustomerQuery = { __typename?: 'Query' } & {
activeCustomer?: Maybe< activeCustomer?: Maybe<
{ __typename?: 'Customer' } & Pick< { __typename?: 'Customer' } & Pick<
Customer, Customer,
'id' | 'firstName' | 'lastName' | 'emailAddress' | 'addresses' 'id' | 'firstName' | 'lastName' | 'emailAddress' | 'addresses' | 'phoneNumber'
> >
> >
} }

View File

@@ -0,0 +1,14 @@
export const updateCustomerAddress = /* GraphQL */ `
mutation updateCustomerAddress($input: UpdateAddressInput!){
updateCustomerAddress(input: $input){
__typename
...on Address{
id
streetLine1
city
postalCode
province
}
}
}
`

View File

@@ -0,0 +1,13 @@
export const updateCustomer = /* GraphQL */ `
mutation updateCustomer($input: UpdateCustomerInput!){
updateCustomer(input:$input){
__typename
...on Customer{
id
firstName
lastName
phoneNumber
}
}
}
`

View File

@@ -5,6 +5,13 @@ export const activeCustomerQuery = /* GraphQL */ `
firstName firstName
lastName lastName
emailAddress emailAddress
phoneNumber
addresses{
streetLine1
city
province
postalCode
}
} }
} }
` `

View File

@@ -1,14 +1,15 @@
export const userInfoQuery = /* GraphQL */ ` export const userInfoQuery = /* GraphQL */ `
query activeCustomer{ query activeCustomer{
activeCustomer{ activeCustomer{
lastName
firstName
emailAddress emailAddress
phoneNumber
addresses{ addresses{
name:fullName streetLine1
address:streetLine1
city city
state:province province
postalCode postalCode
phoneNumber
} }
} }
} }

View File

@@ -11,7 +11,7 @@
width: 20.6rem; width: 20.6rem;
.selectTrigger { .selectTrigger {
width: 20.6rem; width: 20.6rem;
padding: 1.2rem 1.6rem; padding: 1.6rem;
} }
} }
&.large { &.large {

View File

@@ -1,10 +1,12 @@
import s from './SelectCommon.module.scss' import s from './SelectCommon.module.scss'
import classNames from 'classnames' import classNames from 'classnames'
import { useState } from 'react' import { useEffect, useState } from 'react'
import { IconVectorDown } from 'src/components/icons' import { IconVectorDown } from 'src/components/icons'
import SelectOption from './SelectOption/SelectOption' import SelectOption from './SelectOption/SelectOption'
interface Props { interface Props {
selected?:string|null,
initValue?:string|null,
placeholder? : string, placeholder? : string,
size?: 'base' | 'large', size?: 'base' | 'large',
type?: 'default' | 'custom', type?: 'default' | 'custom',
@@ -12,10 +14,14 @@ interface Props {
onChange?: (value: string) => void, onChange?: (value: string) => void,
} }
const SelectCommon = ({ type = 'default', size = 'base', option, placeholder, onChange}: Props) => { const SelectCommon = ({selected,initValue, type = 'default', size = 'base', option, placeholder, onChange}: Props) => {
const [selectedName, setSelectedName] = useState(placeholder) const [selectedName, setSelectedName] = useState(placeholder)
const [selectedValue, setSelectedValue] = useState('') const [selectedValue, setSelectedValue] = useState('')
useEffect(()=>{
setSelectedValue(selected ?? '');
setSelectedValue(initValue ?? '');
})
const changeSelectedName = (item:string, value: string) => { const changeSelectedName = (item:string, value: string) => {
setSelectedValue(value) setSelectedValue(value)
setSelectedName(item) setSelectedName(item)

View File

@@ -5,7 +5,16 @@ import useSWR from 'swr'
const useActiveCustomer = () => { const useActiveCustomer = () => {
const { data, ...rest } = useSWR<ActiveCustomerQuery>([activeCustomerQuery], gglFetcher) const { data, ...rest } = useSWR<ActiveCustomerQuery>([activeCustomerQuery], gglFetcher)
return { customer: data?.activeCustomer, ...rest } return {
customer: data?.activeCustomer,
userInfo:{
firstName: data?.activeCustomer?.firstName,
lastName:data?.activeCustomer?.lastName,
email:data?.activeCustomer?.emailAddress,
phoneNumber: data?.activeCustomer?.phoneNumber,
address: data?.activeCustomer?.addresses?.[0]
},
...rest }
} }
export default useActiveCustomer export default useActiveCustomer

View File

@@ -4,7 +4,6 @@ import { SignupMutation } from '@framework/schema'
import fetcher from 'src/utils/fetcher' import fetcher from 'src/utils/fetcher'
import { CommonError } from 'src/domains/interfaces/CommonError' import { CommonError } from 'src/domains/interfaces/CommonError'
import { signupMutation } from '@framework/utils/mutations/sign-up-mutation' import { signupMutation } from '@framework/utils/mutations/sign-up-mutation'
interface SignupInput { interface SignupInput {
email: string email: string
firstName?: string firstName?: string

View File

@@ -1 +1,4 @@
export { default as useModalCommon } from './useModalCommon' export { default as useModalCommon } from './useModalCommon'
export { default as useEditUserInfo } from './user/useEditUserInfo'
export { default as useEditCustomerAddress } from './user/useEditCustomerAddress'

View File

@@ -0,0 +1,55 @@
import { Address } from '@framework/schema'
import { updateCustomerAddress } from '@framework/utils/mutations/update-customer-address-mutation'
import { useState } from 'react'
import fetcher from 'src/utils/fetcher'
import { useActiveCustomer } from '../auth'
interface Props {
address?:string,
city?:string|null,
postalCode?:string|null,
state?:string
}
const useEditCustomerAddress = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const {customer,mutate} = useActiveCustomer();
const editCustomerAddress = (
{ address,city,postalCode,state}: Props,
fCallBack: (isSuccess: boolean, message?: string) => void
) => {
setError(null)
setLoading(true)
fetcher<Address>({
query: updateCustomerAddress,
variables: {
input: {
id:customer?.id,
streetLine1:address,
city,
postalCode,
province:state
},
},
}) .then((data) => {
if(data.updateCustomerAddress.__typename == 'Address'){
mutate();
fCallBack(true)
return data
}
}) .catch((error) => {
setError(error)
fCallBack(false, error.message)
})
.finally(() => setLoading(false))
}
return { loading, editCustomerAddress, error }
}
export default useEditCustomerAddress

View File

@@ -0,0 +1,51 @@
import { useState } from 'react'
import { Customer } from '@framework/schema'
import fetcher from 'src/utils/fetcher'
import { updateCustomer } from '@framework/utils/mutations/update-customer-mutation'
import { useActiveCustomer } from '../auth'
interface Props {
firstName?: string;
lastName?: string,
phoneNumber?:string,
}
const useEditUserInfo = () => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const {mutate} = useActiveCustomer();
const editUserInfo = (
{ firstName,lastName,phoneNumber}: Props,
fCallBack: (isSuccess: boolean, message?: string) => void
) => {
setError(null)
setLoading(true)
fetcher<Customer>({
query: updateCustomer,
variables: {
input: {
firstName,
lastName,
phoneNumber
},
},
})
.then((data) => {
if (data.updateCustomer.__typename == 'Customer') {
mutate();
return data
}
})
.catch((error) => {
setError(error)
fCallBack(false, error.message)
})
.finally(() => setLoading(false))
}
return { loading, editUserInfo, error }
}
export default useEditUserInfo

View File

@@ -1,12 +0,0 @@
import { ActiveCustomerQuery } from '@framework/schema'
import { userInfoQuery } from '@framework/utils/queries/user-info-query'
import gglFetcher from 'src/utils/gglFetcher'
import useSWR from 'swr'
const useUserInfo = () => {
const { data } = useSWR<ActiveCustomerQuery>([userInfoQuery], gglFetcher)
return { userInfo: data?.activeCustomer}
}
export default useUserInfo

View File

@@ -13,7 +13,6 @@ import { PRODUCT_CART_DATA_TEST } from 'src/utils/demo-data';
import { ACCOUNT_TAB, QUERY_KEY } from "src/utils/constanst.utils" import { ACCOUNT_TAB, QUERY_KEY } from "src/utils/constanst.utils"
import { useRouter } from "next/router" import { useRouter } from "next/router"
import { useActiveCustomer } from 'src/components/hooks/auth' import { useActiveCustomer } from 'src/components/hooks/auth'
import useUserInfo from "src/components/hooks/user/useUserInfo"
import { AccountProps } from "./components/AccountInfomation/AccountInfomation" import { AccountProps } from "./components/AccountInfomation/AccountInfomation"
const waiting = [ const waiting = [
{ {
@@ -54,17 +53,6 @@ const delivered = [
} }
] ]
let account = {
firstName: "Nhân",
lastName: "Trần",
email: "vuduong@gmail.com",
address: "235 Dien Bien Phu Bis, Dakao ward",
state: "District 1",
city: "HCMC",
postalCode: "700000",
phoneNumber: "(+84) 937 937 195"
}
interface AccountPageProps { interface AccountPageProps {
defaultActiveContent?: "info" | "orders" | "favorites" defaultActiveContent?: "info" | "orders" | "favorites"
} }
@@ -84,18 +72,8 @@ const getTabIndex = (tab?: string): number => {
const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => { const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
const router = useRouter() const router = useRouter()
const {userInfo} = useUserInfo(); const {userInfo} = useActiveCustomer();
// const email = userInfo?.emailAddress;
// const [info] = userInfo?.addresses || [];
// const accountInfo = {...info,userInfo?.emailAddress};
// const clone:AccountProps = Object.create(accountInfo);
// Object.assign(clone, accountInfo);
console.log();
const [activeTab, setActiveTab] = useState(defaultActiveContent==="info" ? 0 : defaultActiveContent==="orders" ? 1 : 2) const [activeTab, setActiveTab] = useState(defaultActiveContent==="info" ? 0 : defaultActiveContent==="orders" ? 1 : 2)
const [modalVisible, setModalVisible] = useState(false); const [modalVisible, setModalVisible] = useState(false);
@@ -123,7 +101,7 @@ const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
<AccountNavigation defaultActiveIndex={activeTab}> <AccountNavigation defaultActiveIndex={activeTab}>
<TabPane tabName="Customer Information"> <TabPane tabName="Customer Information">
<AccountInfomation account={account} onClick={showModal} /> <AccountInfomation account={userInfo} onClick={showModal} />
</TabPane> </TabPane>
<TabPane tabName="Your Orders"> <TabPane tabName="Your Orders">
<OrderInfomation waiting={waiting} delivering={delivering} delivered={delivered} /> <OrderInfomation waiting={waiting} delivering={delivering} delivered={delivered} />
@@ -133,7 +111,7 @@ const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
</TabPane> </TabPane>
</AccountNavigation> </AccountNavigation>
</section> </section>
<EditInfoModal accountInfo={account} closeModal={closeModal} visible={modalVisible} /> <EditInfoModal accountInfo={userInfo} closeModal={closeModal} visible={modalVisible} />
</> </>
) )
} }

View File

@@ -6,17 +6,21 @@ import avatar from '../../assets/avatar.png'
import { ButtonCommon } from 'src/components/common' import { ButtonCommon } from 'src/components/common'
import { useActiveCustomer } from 'src/components/hooks/auth' import { useActiveCustomer } from 'src/components/hooks/auth'
import { Address } from '@framework/schema'
export interface AccountProps { export interface AccountProps {
name?: string firstName?: string
lastName?: string
email?: string email?: string
address?: string phoneNumber?:string|null
state?: string address?: Address
city?: string
postalCode?: string
phoneNumber?: string
} }
const states = [
{name: "District 1", value: "D1"},
{name: "District 2", value: "D2"},
{name: "District 3", value: "D3"}
]
interface AccountInfomationProps { interface AccountInfomationProps {
account: AccountProps account: AccountProps
onClick: () => void onClick: () => void
@@ -24,11 +28,10 @@ interface AccountInfomationProps {
const AccountInfomation = ({ account, onClick }: AccountInfomationProps) => { const AccountInfomation = ({ account, onClick }: AccountInfomationProps) => {
const { customer } = useActiveCustomer() const { customer } = useActiveCustomer()
// need to handle call back when edit account information // need to handle call back when edit account information
const showEditForm = () => onClick() const showEditForm = () => onClick()
const state = states.find((val)=>val.value == account.address?.province);
return ( return (
<section className={s.accountInfomation}> <section className={s.accountInfomation}>
<div className={s.avatar}> <div className={s.avatar}>
@@ -45,8 +48,8 @@ const AccountInfomation = ({ account, onClick }: AccountInfomationProps) => {
<div className={s.shippingInfo}>Shipping Infomation</div> <div className={s.shippingInfo}>Shipping Infomation</div>
<div className={s.accountAddress}> <div className={s.accountAddress}>
{account.address + {account.address?.streetLine1 +
`, ${account.state}, ${account.city}, ${account.postalCode}`} `, ${state?.name}, ${account.address?.city}, ${account.address?.postalCode}`}
</div> </div>
<div className={s.accountPhoneNumber}>{account.phoneNumber}</div> <div className={s.accountPhoneNumber}>{account.phoneNumber}</div>

View File

@@ -1,6 +1,15 @@
@import '../../../../../../styles/utilities'; @import '../../../../../../styles/utilities';
.editInfoModal { .editInfoModal {
.u-form{
width: 60rem;
}
.inputName{
@apply flex justify-between;
.input{
width: 48.5%;
}
}
.input { .input {
@apply bg-white; @apply bg-white;
margin-bottom: 1.6rem; margin-bottom: 1.6rem;
@@ -23,6 +32,7 @@
.inputPostalCode { .inputPostalCode {
@apply bg-white; @apply bg-white;
margin-left: 0.8rem; margin-left: 0.8rem;
width: 100%;
} }
.inputPhoneNumber { .inputPhoneNumber {

View File

@@ -1,19 +1,34 @@
import React from "react" import React, { useState } from "react"
import s from './EditInfoModal.module.scss' import s from './EditInfoModal.module.scss'
import { ModalCommon, Inputcommon, SelectCommon, ButtonCommon } from '../../../../../common' import { ModalCommon, SelectCommon, ButtonCommon } from '../../../../../common'
import { Address } from "@framework/schema";
import {
InputFiledInForm,
} from 'src/components/common'
import * as Yup from 'yup'
import { Form, Formik } from 'formik'
import { useEditCustomerAddress, useEditUserInfo } from "src/components/hooks";
import { LANGUAGE } from 'src/utils/language.utils'
import { useMessage } from 'src/components/contexts'
interface EditInfoModalProps { interface EditInfoModalProps {
accountInfo: {firstName: string,lastName: string, email: string, address: string, state: string, city: string, postalCode: string, phoneNumber: string}; accountInfo: {
firstName?: string
lastName?: string
email?: string
phoneNumber?:string|null
address?: Address
};
visible: boolean; visible: boolean;
closeModal: () => void; closeModal: () => void;
} }
const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoModalProps) => { const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoModalProps) => {
const [stateValue,setStateValue] = useState('');
const { loading, editUserInfo } = useEditUserInfo();
const {editCustomerAddress} = useEditCustomerAddress();
const { showMessageSuccess, showMessageError } = useMessage()
function saveInfo() {
closeModal();
}
const states = [ const states = [
{name: "District 1", value: "D1"}, {name: "District 1", value: "D1"},
@@ -21,43 +36,165 @@ const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoMod
{name: "District 3", value: "D3"} {name: "District 3", value: "D3"}
] ]
const DisplayingErrorMessagesSchema = Yup.object().shape({
firstName: Yup.string().required('Required'),
lastName: Yup.string().required('Required'),
address: Yup.string().required('Required'),
city: Yup.string().required('Required'),
postalCode: Yup.string(),
phoneNumber: Yup.string(),
})
function onEditUserInfo (
values: {
firstName: string|undefined;
lastName: string|undefined,
address:string|undefined,
city?:string|null,
postalCode?:string|null,
phoneNumber?:string|null
}) {
editUserInfo(
{
firstName: values.firstName,
lastName: values.lastName,
phoneNumber:values.phoneNumber ?? '',
},onChangUserInfoCallBack);
editCustomerAddress(
{
address: values.address ,
city:values.city,
postalCode:values.postalCode,
state:stateValue
},
onChangUserInfoCallBack);
}
function onChangUserInfoCallBack(isSuccess: boolean, message?: string){
if (isSuccess) {
closeModal();
showMessageSuccess("Change Your Information Successfully.", 15000)
} else {
showMessageError(LANGUAGE.MESSAGE.ERROR)
}
}
function state(state:string){
setStateValue(state);
}
return ( return (
<ModalCommon onClose={closeModal} visible={visible} title="Edit Infomation"> <ModalCommon onClose={closeModal} visible={visible} title="Edit Infomation">
<section className={s.editInfoModal}> <section className={s.editInfoModal}>
<div className={s.input}> <Formik
<Inputcommon placeholder="Name" value={accountInfo.firstName} type="text" /> initialValues={
</div> {
<div className={s.input}> firstName:accountInfo.firstName,
<Inputcommon placeholder="Name" value={accountInfo.lastName} type="text" /> lastName: accountInfo.lastName,
</div> address:accountInfo.address?.streetLine1,
city: accountInfo.address?.city,
<div className={s.input}> postalCode: accountInfo.address?.postalCode,
<Inputcommon placeholder="Address" value={accountInfo.address} type="text" /> phoneNumber:accountInfo.phoneNumber
</div> }}
validationSchema={DisplayingErrorMessagesSchema}
<div className={s.input}> onSubmit={onEditUserInfo}
<Inputcommon placeholder="City" value={accountInfo.city} type="text" /> >
</div> {({ errors, touched, isValid, submitForm }) => (
<Form className="u-form">
<div className={s.inputName}>
<div className="flex"> <div className={s.input}>
<div className={s.inputState}> <InputFiledInForm
<SelectCommon type="custom" placeholder="State" option={states} /> name="firstName"
placeholder="First Name"
error={
touched.firstName && errors.firstName
? errors.firstName.toString()
: ''
}
isShowIconSuccess={touched.firstName && !errors.firstName}
/>
</div>
<div className={s.input}>
<InputFiledInForm
name="lastName"
placeholder="Last Name"
error={
touched.lastName && errors.lastName
? errors.lastName.toString()
: ''
}
isShowIconSuccess={touched.lastName && !errors.lastName}
/>
</div>
</div> </div>
<div className={s.inputPostalCode}> <div className={s.input}>
<Inputcommon placeholder="Postal code" value={accountInfo.postalCode} type="text" /> <InputFiledInForm
name="address"
placeholder="Address"
error={
touched.address && errors.address
? errors.address.toString()
: ''
}
isShowIconSuccess={touched.address && !errors.address}
/>
</div> </div>
</div>
<div className={s.input}>
<InputFiledInForm
name="city"
placeholder="City"
error={
touched.city && errors.city
? errors.city.toString()
: ''
}
isShowIconSuccess={touched.city && !errors.city}
/>
</div>
<div className={s.inputPhoneNumber}> <div className="flex">
<Inputcommon placeholder="Phone number" value={accountInfo.phoneNumber} type="text" /> <div className={s.inputState}>
</div> <SelectCommon initValue={accountInfo.address?.province} selected={accountInfo.address?.province} type="custom" onChange={state} placeholder="State" option={states} size="large"/>
</div>
<div className={s.buttons}> <div className={s.inputPostalCode}>
<ButtonCommon onClick={closeModal} type="light" size="large" >Cancel</ButtonCommon> <InputFiledInForm
<ButtonCommon onClick={saveInfo} size="large" >Save</ButtonCommon> name="postalCode"
</div> placeholder="Postal code"
error={
touched.postalCode && errors.postalCode
? errors.postalCode.toString()
: ''
}
isShowIconSuccess={touched.postalCode && !errors.postalCode}
/>
</div>
</div>
<div className={s.inputPhoneNumber}>
<InputFiledInForm
name="phoneNumber"
placeholder="Phone number"
error={
touched.phoneNumber && errors.phoneNumber
? errors.phoneNumber.toString()
: ''
}
isShowIconSuccess={touched.phoneNumber && !errors.phoneNumber}
onEnter={isValid ? submitForm : undefined}
/>
</div>
<div className={s.buttons}>
<ButtonCommon onClick={closeModal} type="light" size="large" >Cancel</ButtonCommon>
<ButtonCommon HTMLType="submit" loading={loading} size="large" >Save</ButtonCommon>
</div>
</Form>
)}
</Formik>
</section> </section>
</ModalCommon> </ModalCommon>
) )