Commit db6fb5cb authored by Pavel Mishakov's avatar Pavel Mishakov

lesson 90 done

parent 81c06340
......@@ -2,14 +2,36 @@ import Layout from "./components/Layout/Layout";
import { Routes, Route } from "react-router-dom";
import ProductFormPage from "./containers/ProductFormPage/ProductFormPage";
import ProductsPage from "./containers/ProductsPage/ProductsPage";
import { AppDispatch, AppState } from "./store/store";
import { shallowEqual, useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
import { checkToken } from "./store/users/users.slice";
import Login from "./containers/Login/Login";
import Register from "./containers/Register/Register";
const App = () => {
const dispatch: AppDispatch = useDispatch()
const {isAuth} = useSelector((state: AppState) => state.users, shallowEqual)
useEffect(() => {
dispatch(checkToken())
}, [])
return (
<Layout>
{isAuth
?
<Routes>
<Route path={'/'} element={<h1>HOME</h1>} />
<Route path={'/products'} element={<ProductsPage />} />
<Route path={'/add-product'} element={<ProductFormPage />} />
</Routes>
:
<Routes>
<Route path={'/'} element={<Login />} />
<Route path={'/register'} element={<Register />} />
</Routes>
}
</Layout>
)
}
......
......@@ -35,6 +35,21 @@ class UserApi {
return response
}
}
public checkToken = async (): Promise<IResponse<IUserGetDto | undefined>> => {
try {
const response = await instance.get(`/users/token`)
return response.data
} catch (err: unknown) {
const error = err as Error
const response: IResponse<undefined> = {
status: EStatuses.NOT_OK,
result: undefined,
message: error.message
}
return response
}
}
}
export const userApi = new UserApi()
\ No newline at end of file
import React, { FunctionComponent, ReactElement } from "react";
import { shallowEqual, useSelector } from "react-redux";
import { NavLink } from "react-router-dom";
import styles from './Header.module.css'
import { AppState } from "../../store/store";
import styles from "./Header.module.css";
const Header: FunctionComponent = (): ReactElement => {
return (
<header className={styles.Header}>
<NavLink
className={styles.Header__link}
to={'/add-product'}
>
Add product
</NavLink>
<NavLink
className={styles.Header__link}
to={'/products'}
>
Products
</NavLink>
</header>
)
}
const {isAuth} = useSelector((state: AppState) => state.users, shallowEqual)
return (
<header className={styles.Header}>
{isAuth ? (
<>
<NavLink className={styles.Header__link} to={"/add-product"}>
Add product
</NavLink>
<NavLink className={styles.Header__link} to={"/products"}>
Products
</NavLink>
</>
) : (
<>
<NavLink className={styles.Header__link} to={"/register"}>
Sign up
</NavLink>
<NavLink className={styles.Header__link} to={"/"}>
Login
</NavLink>
</>
)}
</header>
);
};
export default Header
\ No newline at end of file
export default Header;
......@@ -5,9 +5,10 @@ import styles from './Layout.module.css'
const Layout: FunctionComponent<ILayoutProps> = (props): ReactElement => {
return (
<div className={styles.Layout}>
<Header />
<Header/>
<main>
{props.children}
</main>
......
.Login {
padding: 20px;
margin: 0 auto;
max-width: 1000px;
border: 1px solid black;
border-radius: 20px;
display: flex;
flex-direction: column;
box-sizing: border-box;
align-items: center;
}
.Login__input {
border: none;
background: lightgrey;
padding: 5px 10px;
border-radius: 20px;
width: 100%;
margin: 10px;
}
.Login__button {
border: none;
background: lightgreen;
padding: 5px 10px;
border-radius: 20px;
margin: 10px 0;
align-self: flex-end;
cursor: pointer;
}
\ No newline at end of file
import React, { useState } from 'react'
import IUserDto from '../../interfaces/IUserCreateDto'
import styles from './Login.module.css'
import {Link} from 'react-router-dom'
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
import { AppDispatch, AppState } from '../../store/store'
import {login} from '../../store/users/users.slice'
const Login: React.FunctionComponent = (): React.ReactElement => {
const {messageUser} = useSelector((state: AppState) => state.users, shallowEqual)
const dispatch: AppDispatch = useDispatch()
const [values, setValues] = useState<IUserDto>({
username: '',
password: ''
})
const inputHandler = (e: React.ChangeEvent<HTMLInputElement>): void => {
setValues(prevState => {
return {...prevState, [e.target.name]: e.target.value}
})
}
const loginHandler = async (e: React.FormEvent) => {
e.preventDefault()
dispatch(login(values))
}
return (
<>
<div>
<form onSubmit={loginHandler} className={styles.Login}>
<span>{messageUser}</span>
<input onChange={inputHandler} value={values.username} placeholder='Username' className={styles.Login__input} type="text" name={'username'} required />
<input onChange={inputHandler} value={values.password} placeholder='Password' className={styles.Login__input} type="password" name={'password'} required />
<button className={styles.Login__button}>Sign in</button>
<Link to={'/register'} className={styles.Login__button}>Sign up now</Link>
</form>
</div>
</>
)
}
export default Login
\ No newline at end of file
.Register {
padding: 20px;
margin: 0 auto;
max-width: 1000px;
border: 1px solid black;
border-radius: 20px;
display: flex;
flex-direction: column;
box-sizing: border-box;
align-items: center;
}
.Register__input {
border: none;
background: lightgrey;
padding: 5px 10px;
border-radius: 20px;
width: 100%;
margin: 10px;
}
.Register__button {
border: none;
background: lightgreen;
padding: 5px 10px;
border-radius: 20px;
margin: 10px 0;
align-self: flex-end;
cursor: pointer;
}
\ No newline at end of file
import React, { useState } from 'react'
import IUserDto from '../../interfaces/IUserCreateDto'
import styles from './Register.module.css'
import {useNavigate} from 'react-router-dom'
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
import { AppDispatch, AppState } from '../../store/store'
import {register} from '../../store/users/users.slice'
const Register: React.FunctionComponent = (): React.ReactElement => {
const {messageUser, isAuth} = useSelector((state: AppState) => state.users, shallowEqual)
const navigate = useNavigate()
const dispatch: AppDispatch = useDispatch()
const [values, setValues] = useState<IUserDto>({
username: '',
password: ''
})
const inputHandler = (e: React.ChangeEvent<HTMLInputElement>): void => {
setValues(prevState => {
return {...prevState, [e.target.name]: e.target.value}
})
}
const registerHandler = async (e: React.FormEvent) => {
e.preventDefault()
dispatch(register(values))
isAuth && navigate('/')
}
return (
<>
<div>
<form onSubmit={registerHandler} className={styles.Login}>
<span>{messageUser}</span>
<input onChange={inputHandler} value={values.username} placeholder='Username' className={styles.Login__input} type="text" name={'username'} required />
<input onChange={inputHandler} value={values.password} placeholder='Password' className={styles.Login__input} type="password" name={'password'} required />
<button className={styles.Login__button}>Sign Up</button>
</form>
</div>
</>
)
}
export default Register
\ No newline at end of file
import IUserGetDto from "../../interfaces/IUserGetDto";
export default interface IUsersState {
user: IUserGetDto
user: IUserGetDto | undefined
isAuth: boolean
loadingUser: boolean
messageUser: string
}
\ No newline at end of file
import { createSlice } from "@reduxjs/toolkit";
import { userApi } from "../../api/userApi";
import IUser from "../../interfaces/IUser";
import IUserCreateDto from "../../interfaces/IUserCreateDto";
import { createAppAsyncThunk } from "../createAppAsyncThunk";
import IUsersState from "./IUsersState";
const namespace = 'users'
export const login = createAppAsyncThunk(
`${namespace}/login`,
async (user: IUserCreateDto) => {
return userApi.login(user)
}
)
export const register = createAppAsyncThunk(
`${namespace}/register`,
async (user: IUserCreateDto) => {
return userApi.register(user)
}
)
export const checkToken = createAppAsyncThunk(
`${namespace}/checkToken`,
async () => {
return userApi.checkToken()
}
)
export const usersSlice = createSlice({
name: namespace,
initialState: {
user: {} as IUser,
isAuth: false
isAuth: false,
loadingUser: false,
messageUser: ''
} as IUsersState,
reducers: {},
extraReducers: (builder) => {
// builder
// .addCase()
builder
.addCase(login.rejected, (state) => {
state.loadingUser = false
})
.addCase(login.pending, (state) => {
state.loadingUser = true
})
.addCase(login.fulfilled, (state, action) => {
state.loadingUser = false
const user = action.payload.result
state.user = user
state.messageUser = action.payload.message
if (user) {
localStorage.setItem('token', user.token)
state.isAuth = true
}
})
.addCase(register.rejected, (state) => {
state.loadingUser = false
})
.addCase(register.pending, (state) => {
state.loadingUser = true
})
.addCase(register.fulfilled, (state, action) => {
state.loadingUser = false
const user = action.payload.result
state.user = user
state.messageUser = action.payload.message
if (user) {
localStorage.setItem('token', user.token)
state.isAuth = true
}
})
.addCase(checkToken.rejected, (state) => {
state.loadingUser = false
})
.addCase(checkToken.pending, (state) => {
state.loadingUser = true
})
.addCase(checkToken.fulfilled, (state, action) => {
state.loadingUser = false
const user = action.payload.result
state.user = user
state.messageUser = action.payload.message
if (user) {
state.isAuth = true
}
})
}
})
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment