import { configureStore } from '@reduxjs/toolkit'
import type { TypedUseSelectorHook } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'
// Import Reducers
import authReducer from './reducers/authReducer'
const store = configureStore({
reducer: {
auth: authReducer,
},
});
// Declare Typed Definitions
type RootState = ReturnType<typeof store.getState>
type AppDispatch = typeof store.dispatch
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
export default store;
import axios from 'axios'
import { createAsyncThunk } from '@reduxjs/toolkit'
import { AUTH } from '../../App.config'
// Import Reducers
import { setIsAuthenticated, setIsAuthenticating, setToken, setUser, setMessage } from '../reducers/authReducer'
// Login Action
export const login = createAsyncThunk('auth/login', async (user: any, { dispatch }) => {
// Set Is Authenticating `true`
dispatch( setIsAuthenticating(true) )
try {
const res = await axios.post(AUTH.LOGIN, user)
// If Error or Token Doesn't Exist
if(!res?.data?.data) {
throw new Error('Token Not Found')
}
const token = res.data.data
// Validate User By Token
dispatch( validateUser(token) )
} catch(err) {
// Dispatch `authReducer` Values to Redux Store
dispatch( setIsAuthenticated(false) )
dispatch( setToken(null) )
dispatch( setUser({}) )
dispatch( setMessage({type: "error", message: err?.response?.data?.message}) )
// Set Is Authenticating `false`
dispatch( setIsAuthenticating(false) )
}
})
// Validate User By Token
export const validateUser = createAsyncThunk('auth/validateUser', async (token: any, { dispatch }) => {
// Set Is Authenticating `true`
dispatch( setIsAuthenticating(true) )
try {
// If Token Doesn't Exist
if(!token) {
throw new Error('User Not Found')
}
const res = await axios.get(AUTH.GET_USER, { headers: { Authorization: `Bearer ${ token }` } })
// If Error or User Doesn't Exist
if(!res?.data?.user) {
throw new Error('User Not Found')
}
const user = res.data.user
// Save `token` & `user` to localStorage
localStorage.setItem('token', token)
localStorage.setItem('user', JSON.stringify(user))
// Dispatch `authReducer` Values to Redux Store
dispatch( setIsAuthenticated(true) )
dispatch( setToken(token) )
dispatch( setUser(user) )
// Set Is Authenticating `false`
dispatch( setIsAuthenticating(false) )
} catch(err) {
console.error(err)
// Dispatch `authReducer` Values to Redux Store
dispatch( setIsAuthenticated(false) )
dispatch( setToken(null) )
dispatch( setUser({}) )
// Set Is Authenticating `false`
dispatch( setIsAuthenticating(false) )
}
})
// Logout Action
export const logout = createAsyncThunk('auth/logout', async (e, { dispatch }) => {
// Set Is Authenticating `true`
dispatch( setIsAuthenticating(true) )
// Clear localStorage
localStorage.clear()
// Dispatch `authReducer` Values to Redux Store
dispatch( setIsAuthenticated(false) )
dispatch( setToken(null) )
dispatch( setUser({}) )
// Set Is Authenticating `false`
dispatch( setIsAuthenticating(false) )
})
import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
interface Message {
type: string,
message: string
}
interface authState {
isAuthenticated: boolean,
isAuthenticating: boolean,
token: null | string
user: object,
message: Message
}
const initialState: authState = {
isAuthenticated: false,
isAuthenticating: true,
token: null,
user: {},
message:{
type: "",
message: ""
},
}
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
setIsAuthenticated: (state, action: PayloadAction<boolean>) => {
state.isAuthenticated = action.payload
},
setIsAuthenticating: (state, action: PayloadAction<boolean>) => {
state.isAuthenticating = action.payload
},
setToken: (state, action: PayloadAction<null | string>) => {
state.token = action.payload
},
setUser: (state, action: PayloadAction<object>) => {
state.user = action.payload
},
setMessage: (state, action: PayloadAction<Message>) => {
state.message = action.payload;
}
}
})
export const { setIsAuthenticated, setIsAuthenticating, setToken, setUser, setMessage } = authSlice.actions
export default authSlice.reducer
import React, { useEffect } from 'react'
import { useRouter } from 'next/router'
import Image from 'next/image'
import Link from 'next/link'
// Import Components
import { Form, Input, Button, Row, Col, Spin, Checkbox, message } from 'antd'
import Meta from '../components/common/Meta'
// Import Action, Method & Reducers
import { useAppSelector, useAppDispatch } from '../redux/store'
import { login, validateUser } from '../redux/actions/authActions'
import { setMessage } from '../redux/reducers/authReducer'
// Import Images
import leftCover from '../public/images/auth/login/login-left-cover.svg'
import line from '../public/images/auth/line.svg'
// Import Icons
import emailIcon from '../public/icons/email.svg'
const Login: React.FC = () => {
// States
const router = useRouter()
const dispatch = useAppDispatch()
// Message States
const [messageApi, contextHolder] = message.useMessage()
// Get Data from Redux Store
const isAuthenticated = useAppSelector(state => state?.auth?.isAuthenticated ?? false)
const isAuthenticating = useAppSelector(state => state?.auth?.isAuthenticating ?? true)
const actionMessage = useAppSelector(state => state?.auth?.message ?? {type: null, message: null})
// Message
const _message = (type: any, content: string) => {
messageApi.open({
type,
content,
})
}
// On Load Validate User by Token
useEffect(() => {
const token = localStorage.getItem('token')
dispatch( validateUser(token) )
}, [])
// On Load Redirect User if Authenticated
useEffect(() => {
if(isAuthenticated && !isAuthenticating) {
router.push('/dashboard')
}
}, [ isAuthenticated, isAuthenticating ])
// Loading State
if(isAuthenticated || isAuthenticating) {
return (
<div style={ spinContainerStyles as React.CSSProperties }>
<Spin size='large' />
</div>
)
}
// On Submit
const _onSubmit = (values: any) => {
dispatch(login(values))
setTimeout(() => {
if(actionMessage?.type === "error"){
console.log("error message")
_message("error", actionMessage?.message)
setMessage({type: "", message: ""})
}
}, 500);
}
return (
<>
{contextHolder}
<Meta title="***" description="***" keywords=""/>
<div className='login' style={ containerStyles as React.CSSProperties }>
<Row justify='space-between' align='middle' style={ rowStyles as React.CSSProperties }>
{/* Image */}
<Col xs={0} sm={0} md={12} lg={14}>
<Image style={{width: '100%', maxHeight: '600px'}} src={ leftCover} alt='login-cover'/>
</Col>
{/* Form */}
<Col xs={24} sm={24} md={12} lg={10}>
<Form
layout='vertical'
style={ formStyles as React.CSSProperties }
onFinish={ _onSubmit }
>
<Row gutter={ [16, 16] }>
{/* Title */}
<h2 style={{ textAlign: 'center' }}>Sign in to ***</h2>
{/* Email */}
<Col span={24}>
<Form.Item
name='email'
label='Email'
rules={[
{
required: true,
message: 'This field is required'
}
]}
>
<Input suffix={ <Image src={ emailIcon } alt='email'/> }/>
</Form.Item>
</Col>
{/* Password */}
<Col span={24}>
<Form.Item
name='password'
label='Password'
rules={[
{
required: true,
message: 'This field is required'
}
]}
>
<Input.Password/>
</Form.Item>
</Col>
{/* Remember Me */}
<Col span={24}>
<Form.Item >
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Checkbox className='remember'>Remember me</Checkbox>
<Link className='forgot' href='' >Forgot Password</Link>
</div>
</Form.Item>
</Col>
{/* Submit Button */}
<Col span={24}>
<Form.Item>
<Button className='submit' htmlType='submit'>SIGN IN</Button>
</Form.Item>
</Col>
{/* Divider */}
<Col span={24}>
<Image style={{ width: '100%' }} src={ line } alt='divider'/>
</Col>
{/* Signup Link */}
<Col span={24} style={ signUpWrapperStyles as React.CSSProperties }>
<p className='text'>{`Don't have an account yet?`}</p> <Link className='signup' href='signup'>Sign Up</Link>
</Col>
</Row>
</Form>
</Col>
</Row>
</div>
</>
)
}
// Styles
const containerStyles = {
width: '100vw',
height: '100vh',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
overflow: 'auto',
}
const rowStyles = {
width: '100%',
height: '100%',
padding: '1rem'
}
const formStyles = {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
maxHeight: '600px',
maxWidth: '450px',
borderRadius: '20px',
boxShadow: '0px 4px 24px -1px rgba(117, 117, 117, 0.25)',
backdropFilter: 'blur(22.5px)',
padding: '20px',
margin: '0 auto'
}
const spinContainerStyles = {
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center'
}
const signUpWrapperStyles = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
gap: '10px'
}
export default Login
';LJHGTFREWQ