Skip to content

Commit

Permalink
Merge pull request #122 from ballyalley-o/development
Browse files Browse the repository at this point in the history
Feat/TCCP117-feature-snackbar-hook: Implement Snackbar hook
  • Loading branch information
ballyalley-o authored Apr 12, 2024
2 parents c0875a1 + eb31322 commit 51c51b8
Show file tree
Hide file tree
Showing 18 changed files with 462 additions and 115 deletions.
60 changes: 60 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"axios": "^1.6.8",
"framer-motion": "^11.0.8",
"markdown-to-jsx": "^7.4.1",
"notistack": "^3.0.1",
"nprogress": "^0.2.0",
"react": "^18.2.0",
"react-avatar-editor": "^13.0.2",
Expand Down
13 changes: 8 additions & 5 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AppNavBar, AppFooter } from 'component'
import { Fallback } from 'page'
import { AuthProvider } from 'auth/auth-provider'
import { SettingProvider } from 'component/setting'
import { SnackProvider } from 'hook'
import ErrorBoundary from 'util/error-boundary'
import Router from 'route'
import withRoot from 'withroot'
Expand All @@ -22,11 +23,13 @@ function App() {
<HelmetProvider>
<BrowserRouter>
<SettingProvider>
<AppNavBar />
<ErrorBoundary fallback={<Fallback fallbackTitle={FALLBACK.BAD_REQUEST.MESSAGE} errorCode={FALLBACK.BAD_REQUEST.CODE} />}>
<Router />
</ErrorBoundary>
<AppFooter />
<SnackProvider>
<AppNavBar />
<ErrorBoundary fallback={<Fallback fallbackTitle={FALLBACK.BAD_REQUEST.MESSAGE} errorCode={FALLBACK.BAD_REQUEST.CODE} />}>
<Router />
</ErrorBoundary>
<AppFooter />
</SnackProvider>
</SettingProvider>
</BrowserRouter>
</HelmetProvider>
Expand Down
48 changes: 21 additions & 27 deletions src/auth/auth-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { LOCAL_STORAGE } from 'constant'
import { COLOR, LOCAL_STORAGE } from 'constant'
import React, { FC, createContext, useEffect, useState, useMemo, useCallback } from 'react'
import { createAsyncThunk } from '@reduxjs/toolkit'
import localStorageSpace from 'util/local-storage-space'
import storage from 'redux-persist/lib/storage'
import { setInitial, setCredential } from 'store/slice/auth'
import { useLoginMutation, useLogoutMutation } from 'store/slice/auth/endpoint'
import { useSelector, dispatch } from 'store'
import { isValidToken, setSession } from 'auth/utility'
import { AuthPath } from 'route/path'
import { RESPONSE } from 'constant'
import { useSelector, dispatch } from 'store'
import { snack } from 'hook'

export const AuthContext = createContext<{
isAuthenticated: boolean
Expand All @@ -32,30 +33,10 @@ export const AuthProvider: FC<AuthProviderProps> = ({ children }) => {
// const [isAuthenticated, setIsAuthenticated] = useState(false)
const { user, isAuthenticated } = useSelector((state: RootState) => state.auth || {})
const [logoutCall] = useLogoutMutation()
const [loginCall, { isLoading }] = useLoginMutation()
const [loginCall, { isLoading, error }] = useLoginMutation()

const storageAvailable = useMemo(() => localStorageSpace(), [])

const token = storageAvailable ? localStorage.getItem(LOCAL_STORAGE.TOKEN) : ''

if (token && isValidToken(token)) {
setSession(token)
}

// useEffect(() => {
// const expirationTime = localStorage.getItem('expirationTime')
// const user = JSON.parse(localStorage.getItem(LOCAL_STORAGE.USER) || '{}')

// if (user.user !== null) {
// dispatch(setCredential({ user: null, isAuthenticated: false, token: null, isInitialized: false }))
// localStorage.removeItem('expirationTime')
// } else {
// console.log('user', user)
// setIsAuthenticated(false)
// dispatch(setCredential({ user: null, isAuthenticated: false, token: null, isInitialized: false }))
// }
// }, [dispatch])

const initialize = useCallback(async () => {
try {
const token = storageAvailable ? localStorage.getItem(LOCAL_STORAGE.TOKEN) : ''
Expand Down Expand Up @@ -116,13 +97,26 @@ export const AuthProvider: FC<AuthProviderProps> = ({ children }) => {
const login = useCallback(
async (credentials: { email: string; password: string; name?: string; token?: string }) => {
const { email, password } = credentials
const res = await loginCall({

if (error) {
snack(error || RESPONSE.error.INVALID_CREDENTIAL)
dispatch(setCredential({ isAuthenticated: false, ...(credentials || {}) }))
throw new Error(RESPONSE.error.INVALID_CREDENTIAL)
}

const res = (await loginCall({
email,
password
}).unwrap()
}).unwrap()) as any

dispatch(setCredential({ isAuthenticated: true, ...(res || {}) }))
// setIsAuthenticated(true)
if (res && res.success && res.user) {
snack(RESPONSE.success.LOGIN, { variant: COLOR.SUCCESS })
dispatch(setCredential({ isAuthenticated: true, ...res }))
} else {
snack(RESPONSE.error.LOGIN_UNABLE, { variant: COLOR.ERROR })
dispatch(setCredential({ isAuthenticated: false, ...(res || {}) }))
throw new Error(RESPONSE.error.INVALID_CREDENTIAL)
}
},
[dispatch]
)
Expand Down
5 changes: 4 additions & 1 deletion src/component/navbar/account-popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useNavigate, Link as RouterLink } from 'react-router-dom'
import { alpha, useTheme } from '@mui/material/styles'
import { Box, Divider, Dialog, Typography, Stack, MenuItem, Link } from '@mui/material'
import { useAuthContext } from 'auth'
import { useSnackbar } from 'hook/use-snack'
import { DefaultAvatar, Avatar } from 'component/avatar'
import { MenuPopover } from 'component/menu-popover'
import { MotionButton } from 'component/motion'
Expand All @@ -22,7 +23,7 @@ export default function AccountPopover({ user }: { user: any }) {
const email = user?.email
const displayName = user?.firstname + ' ' + user?.lastname

// const { enqueueSnackbar } = useSnackbar()
const { enqueueSnackbar: snack } = useSnackbar()

const { themeMode, themeStretch, themeContrast, onResetSetting } = useSettingContext()

Expand All @@ -39,8 +40,10 @@ export default function AccountPopover({ user }: { user: any }) {
if (logout) {
logout()
}
snack('Logout Successful')
handleClosePopover()
} catch (error) {
snack('Unable to logout', { variant: 'error' })
console.error(error)
// enqueueSnackbar('Unable to logout', { variant: 'error' })
}
Expand Down
6 changes: 6 additions & 0 deletions src/config/icon-directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ function _getWebIcon(icon: string) {
const ICON_WEB = {
ALERT_OUTLINE: _getWebIcon('alert-triangle-outline'),
ARROW_FORWARD: _getWebIcon('arrow-ios-forward'),
CHEVRON_RIGHT: _getWebIcon('chevron-right-outline'),
CLOSE: _getWebIcon('close-fill'),
CHECKMARK_CIRCLE: _getWebIcon('checkmark-circle-outline'),
CONTRAST_BOX: _getWebIcon('contrast-box'),
ERROR_OUTLINE: _getWebIcon('alert-circle-outline'),
ERROR: _getWebIcon('alert-circle'),
INFO: _getWebIcon('info'),

Expand Down Expand Up @@ -63,9 +66,12 @@ export enum ICON_WEB_NAME {
// @web
ALERT_OUTLINE = 'ALERT_OUTLINE',
ARROW_FORWARD = 'ARROW_FORWARD',
CHECKMARK_CIRCLE = 'CHECKMARK_CIRCLE',
CHEVRON_RIGHT = 'CHEVRON_RIGHT',
CLOSE = 'CLOSE',
CONTRAST_BOX = 'CONTRAST_BOX',
ERROR = 'ERROR',
ERROR_OUTLINE = 'ERROR_OUTLINE',
EYE_OFF = 'EYE_OFF',
EYE_HIDE = 'EYE_HIDE',
INFO = 'INFO',
Expand Down
1 change: 1 addition & 0 deletions src/constant/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const RESPONSE = {
INVALID_TOKEN: 'Invalid token',
NOT_OWNER: (user: string, course: string) => `User ${user} is unauthorized to update course ${course}`,
ROLE_NOT_ALLOWED: (data: string) => `Current role ${data} is unauthorized to access this route`,
LOGIN_UNABLE: 'Unable to login',
parseErr: (err: any) => `Error parsing JSON: ${err}`,
NotInstance: 'This class cannot be instantiated',
PASSWORD_MATCH: 'Passwords do not match',
Expand Down
6 changes: 3 additions & 3 deletions src/constant/size.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
export enum SIZE {
SMALL = 'small',
MEDIUM = 'medium',
LARGE = 'large',
LARGE = 'large'
}

type SizeMapType = 'small' | 'medium' | 'large'

export const SIZE_MAP: Record<SizeMapType, string> = {
[SIZE.SMALL]: 'small',
[SIZE.MEDIUM]: 'medium',
[SIZE.LARGE]: 'large',
[SIZE.LARGE]: 'large'
}

export type SizeType = {
Expand All @@ -25,5 +25,5 @@ export const SIZE_TYPE: SizeType = {
sm: 'sm',
md: 'md',
lg: 'lg',
xl: 'xl',
xl: 'xl'
}
3 changes: 2 additions & 1 deletion src/hook/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as useLocalStorage } from './use-local-storage'
export * from './use-snack'
export { useResponsive } from './use-responsive'
export * from './use-icon'
export { default as useLocalStorage } from './use-local-storage'
3 changes: 3 additions & 0 deletions src/hook/use-snack/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from 'notistack'
export { enqueueSnackbar as snack } from 'notistack'
export { default as SnackProvider } from './use-snack'
42 changes: 42 additions & 0 deletions src/hook/use-snack/style.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { m } from 'framer-motion'
import { Box } from '@mui/material'
import { styled } from '@mui/material/styles'
import { MaterialDesignContent } from 'notistack'

export const SSnackContent = styled(MaterialDesignContent)(({ theme }) => ({
'&.notistack-MuiContent': {
backgroundColor: theme.palette.common.white,
display: 'flex',
flexDirection: 'row',
objectFit: 'cover',
justifyContent: 'space-evenly',
fontSize: theme.typography.overline.fontSize,
color: theme.palette.common.black,
padding: theme.spacing(2)
},
'&.notistack-MuiContent-error': {
backgroundColor: theme.palette.error.dark,
flexDirection: 'row',
justifyContent: 'space-between',
color: theme.palette.common.white,
padding: 0
},
'&.notistack-MuiContent-success': {
flexDirection: 'row',
padding: 0
},
'&.notistack-MuiContent-warning': {
backgroundColor: theme.palette.common.white,
flexDirection: 'row',
padding: 0
}
}))

export const SSnackIconMDiv = styled(Box)(({ theme, color }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.grey[500],
width: 30,
height: 30
}))
Loading

0 comments on commit 51c51b8

Please sign in to comment.