feat: logout

:%s
This commit is contained in:
lytrankieio123
2021-09-29 16:58:12 +07:00
parent 660ba8d301
commit faabf5228d
12 changed files with 201 additions and 121 deletions

View File

@@ -1,13 +1,16 @@
import React from 'react'; import React from 'react'
import { Layout } from 'src/components/common'; import { Layout } from 'src/components/common'
import { AccountPage } from 'src/components/modules/account'; import useActiveCustomer from 'src/components/hooks/useActiveCustomer'
import { AccountPage, AccountSignIn } from 'src/components/modules/account'
const Account = () => { const Account = () => {
return ( const { customer } = useActiveCustomer()
<AccountPage/> if (customer) {
); return <AccountPage />
}; }
return <AccountSignIn />
}
Account.Layout = Layout Account.Layout = Layout
export default Account; export default Account

View File

@@ -6,115 +6,154 @@ import { ButtonCommon } from 'src/components/common'
import InputSearch from 'src/components/common/InputSearch/InputSearch' import InputSearch from 'src/components/common/InputSearch/InputSearch'
import MenuDropdown from 'src/components/common/MenuDropdown/MenuDropdown' import MenuDropdown from 'src/components/common/MenuDropdown/MenuDropdown'
import { useCartDrawer } from 'src/components/contexts' import { useCartDrawer } from 'src/components/contexts'
import { IconBuy, IconFilter, IconHeart, IconHistory, IconUser } from 'src/components/icons' import {
import { ACCOUNT_TAB, FILTER_PAGE, QUERY_KEY, ROUTE } from 'src/utils/constanst.utils' 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 Logo from '../../../Logo/Logo'
import s from './HeaderMenu.module.scss' import s from './HeaderMenu.module.scss'
import { useLogout } from '../../../../hooks/auth'
interface Props { interface Props {
children?: any, children?: any
isFull?: boolean, isFull?: boolean
isStickyHeader?: boolean, isStickyHeader?: boolean
visibleFilter?: boolean, visibleFilter?: boolean
openModalAuthen: () => void, openModalAuthen: () => void
openModalInfo: () => void, openModalInfo: () => void
toggleFilter: () => void, toggleFilter: () => void
} }
const HeaderMenu = memo(
const HeaderMenu = memo(({ isFull, isStickyHeader, visibleFilter, openModalAuthen, openModalInfo, toggleFilter }: Props) => { ({
isFull,
isStickyHeader,
visibleFilter,
openModalAuthen,
openModalInfo,
toggleFilter,
}: Props) => {
const router = useRouter() const router = useRouter()
const { toggleCartDrawer } = useCartDrawer() const { toggleCartDrawer } = useCartDrawer()
const optionMenu = useMemo(() => [ const { logout } = useLogout()
{
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',
},
], [openModalAuthen]) const optionMenu = useMemo(
return ( () => [
<section className={classNames({ {
[s.headerMenu]: true, onClick: openModalAuthen,
[s.small]: isStickyHeader, name: 'Login (Demo)',
[s.full]: isFull, },
})}> {
<div className={s.left}> onClick: openModalInfo,
<div className={s.top}> name: 'Create User Info (Demo)',
<Logo /> },
<div className={s.iconGroup}> {
{ link: '/account-not-login',
FILTER_PAGE.includes(router.pathname) && ( name: 'Account Not Login (Demo)',
<button className={s.iconFilter} onClick={toggleFilter}> },
<IconFilter /> {
<div className={classNames({ [s.dot]: true, [s.isShow]: visibleFilter })}></div> link: '/demo',
</button> name: 'Notifications Empty (Demo)',
) },
} {
<button className={`${s.iconCart} ${s.btnCart}`} onClick={toggleCartDrawer}> link: ROUTE.NOTIFICATION,
<IconBuy /> name: 'Notifications',
</button> },
</div> {
link: ROUTE.ACCOUNT,
</div> name: 'Account',
<div className={s.searchWrap}> },
<div className={s.inputSearch}> {
<InputSearch /> link: '/',
</div> name: 'Logout',
<div className={s.buttonSearch}> onClick: logout,
<ButtonCommon>Search</ButtonCommon> },
</div> ],
</div> [openModalAuthen, openModalInfo, logout]
</div>
<ul className={s.menu}>
<li>
<Link href={`${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.ORDER}`}>
<a>
<IconHistory />
</a>
</Link>
</li>
<li>
<Link href={`${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.FAVOURITE}`}>
<a className={s.iconFavourite}>
<IconHeart />
</a>
</Link>
</li>
<li>
<MenuDropdown options={optionMenu} isHasArrow={false}><IconUser /></MenuDropdown>
</li>
<li>
<button className={s.btnCart} onClick={toggleCartDrawer}>
<IconBuy />
</button>
</li>
</ul>
</section>
) )
}) return (
<section
className={classNames({
[s.headerMenu]: true,
[s.small]: isStickyHeader,
[s.full]: isFull,
})}
>
<div className={s.left}>
<div className={s.top}>
<Logo />
<div className={s.iconGroup}>
{FILTER_PAGE.includes(router.pathname) && (
<button className={s.iconFilter} onClick={toggleFilter}>
<IconFilter />
<div
className={classNames({
[s.dot]: true,
[s.isShow]: visibleFilter,
})}
></div>
</button>
)}
<button
className={`${s.iconCart} ${s.btnCart}`}
onClick={toggleCartDrawer}
>
<IconBuy />
</button>
</div>
</div>
<div className={s.searchWrap}>
<div className={s.inputSearch}>
<InputSearch />
</div>
<div className={s.buttonSearch}>
<ButtonCommon>Search</ButtonCommon>
</div>
</div>
</div>
<ul className={s.menu}>
<li>
<Link
href={`${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.ORDER}`}
>
<a>
<IconHistory />
</a>
</Link>
</li>
<li>
<Link
href={`${ROUTE.ACCOUNT}?${QUERY_KEY.TAB}=${ACCOUNT_TAB.FAVOURITE}`}
>
<a className={s.iconFavourite}>
<IconHeart />
</a>
</Link>
</li>
<li>
<MenuDropdown options={optionMenu} isHasArrow={false}>
<IconUser />
</MenuDropdown>
</li>
<li>
<button className={s.btnCart} onClick={toggleCartDrawer}>
<IconBuy />
</button>
</li>
</ul>
</section>
)
}
)
HeaderMenu.displayName = 'HeaderMenu'
export default HeaderMenu export default HeaderMenu

View File

@@ -24,7 +24,7 @@ const MenuNavigationProductList = ({categories,brands,featured,visible,onClose}:
setDataSort({...dataSort,...value}); setDataSort({...dataSort,...value});
} }
function filter(){ function filter(){
console.log(dataSort) // console.log(dataSort)
} }
return( return(
<> <>

View File

@@ -0,0 +1,3 @@
export { default as useLogout } from './useLogout'

View File

@@ -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<CommonError | null>(null)
const { mutate } = useActiveCustomer()
const logout = () => {
setError(null)
setLoading(true)
rawFetcher<LogoutMutation>({
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

View File

@@ -4,6 +4,7 @@ import useActiveCustomer from './useActiveCustomer'
import { CommonError } from 'src/domains/interfaces/CommonError' import { CommonError } from 'src/domains/interfaces/CommonError'
import rawFetcher from 'src/utils/rawFetcher' import rawFetcher from 'src/utils/rawFetcher'
import { LoginMutation } from '@framework/schema' import { LoginMutation } from '@framework/schema'
import { LOCAL_STORAGE_KEY } from 'src/utils/constanst.utils'
const query = gql` const query = gql`
mutation login($username: String!, $password: String!) { mutation login($username: String!, $password: String!) {
@@ -42,9 +43,8 @@ const useLogin = () => {
throw CommonError.create(data.login.message, data.login.errorCode) throw CommonError.create(data.login.message, data.login.errorCode)
} }
const authToken = headers.get('vendure-auth-token') const authToken = headers.get('vendure-auth-token')
console.log("auth token: ", authToken)
if (authToken != null) { if (authToken != null) {
localStorage.setItem('token', authToken) localStorage.setItem(LOCAL_STORAGE_KEY.TOKEN, authToken)
return mutate() return mutate()
} }
}) })

View File

@@ -87,7 +87,7 @@ const AccountPage = ({ defaultActiveContent="orders" } : AccountPageProps) => {
const query = router.query[QUERY_KEY.TAB] as string const query = router.query[QUERY_KEY.TAB] as string
const index = getTabIndex(query) const index = getTabIndex(query)
setActiveTab(index) setActiveTab(index)
}, [router.query[QUERY_KEY.TAB]]) }, [router.query])
function showModal() { function showModal() {
setModalVisible(true); setModalVisible(true);

View File

@@ -12,8 +12,6 @@ interface EditInfoModalProps {
const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoModalProps) => { const EditInfoModal = ({ accountInfo, visible = false, closeModal }: EditInfoModalProps) => {
function saveInfo() { function saveInfo() {
console.log("saved !!!");
closeModal(); closeModal();
} }

View File

@@ -16,7 +16,6 @@ const FormSubscribe = () => {
e.preventDefault && e.preventDefault() e.preventDefault && e.preventDefault()
value = inputElementRef.current?.getValue()?.toString() || '' value = inputElementRef.current?.getValue()?.toString() || ''
} }
console.log("email here: ", value)
} }
return ( return (

View File

@@ -38,6 +38,10 @@ export const ACCOUNT_TAB = {
FAVOURITE: 'wishlist', FAVOURITE: 'wishlist',
} }
export const LOCAL_STORAGE_KEY = {
TOKEN: 'token'
}
export const QUERY_KEY = { export const QUERY_KEY = {
TAB: 'tab', TAB: 'tab',
CATEGORY: 'category', CATEGORY: 'category',

View File

@@ -1,5 +1,6 @@
import { request } from 'graphql-request' import { request } from 'graphql-request'
import { RequestDocument, Variables } from 'graphql-request/dist/types' import { RequestDocument, Variables } from 'graphql-request/dist/types'
import { LOCAL_STORAGE_KEY } from './constanst.utils'
interface QueryOptions { interface QueryOptions {
query: RequestDocument query: RequestDocument
@@ -10,11 +11,7 @@ interface QueryOptions {
const fetcher = async <T>(options: QueryOptions): Promise<T> => { const fetcher = async <T>(options: QueryOptions): Promise<T> => {
const { query, variables } = options const { query, variables } = options
// console.log('query') const token = localStorage.getItem(LOCAL_STORAGE_KEY.TOKEN)
// console.log(options)
const token = localStorage.getItem('token')
// console.log('token')
// console.log(token)
const res = await request<T>( const res = await request<T>(
process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL as string, process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL as string,
query, query,

View File

@@ -1,5 +1,6 @@
import { rawRequest } from 'graphql-request' import { rawRequest } from 'graphql-request'
import { RequestDocument, Variables } from 'graphql-request/dist/types' import { RequestDocument, Variables } from 'graphql-request/dist/types'
import { LOCAL_STORAGE_KEY } from './constanst.utils'
interface QueryOptions { interface QueryOptions {
query: RequestDocument query: RequestDocument
@@ -14,10 +15,12 @@ const rawFetcher = <T>({
onLoad = () => true, onLoad = () => true,
}: QueryOptions): Promise<{ data: T; headers: any }> => { }: QueryOptions): Promise<{ data: T; headers: any }> => {
onLoad(true) onLoad(true)
const token = localStorage.getItem(LOCAL_STORAGE_KEY.TOKEN)
return rawRequest<T>( return rawRequest<T>(
process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL as string, process.env.NEXT_PUBLIC_VENDURE_SHOP_API_URL as string,
query as string, query as string,
variables variables,
token ? { Authorization: 'Bearer ' + token } : {}
) )
.then(({ data, headers }) => { .then(({ data, headers }) => {
return { data, headers } return { data, headers }