mirror of
https://github.com/vercel/commerce.git
synced 2025-07-22 20:26:49 +00:00
✨ feat: logout
:%s
This commit is contained in:
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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(
|
||||||
<>
|
<>
|
||||||
|
3
src/components/hooks/auth/index.ts
Normal file
3
src/components/hooks/auth/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export { default as useLogout } from './useLogout'
|
||||||
|
|
||||||
|
|
34
src/components/hooks/auth/useLogout.tsx
Normal file
34
src/components/hooks/auth/useLogout.tsx
Normal 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
|
@@ -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()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -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);
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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 (
|
||||||
|
@@ -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',
|
||||||
|
@@ -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,
|
||||||
|
@@ -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 }
|
||||||
|
Reference in New Issue
Block a user