Commit 6025b9c0 authored by Egor Kremnev's avatar Egor Kremnev

add register user

parent 0ecb4814
...@@ -12,7 +12,14 @@ const UserSchema = new Schema({ ...@@ -12,7 +12,14 @@ const UserSchema = new Schema({
username: { username: {
type: String, type: String,
required: true, required: true,
unique: true unique: true,
validate: {
validator: async (username) => {
const user = await User.findOne({username});
return !user;
},
message: "This user is already exists"
}
}, },
token: { token: {
type: String, type: String,
......
import {Route, Routes} from "react-router-dom"; import {Route, Routes} from "react-router-dom";
import {PRODUCT_ADD, PRODUCT_LIST, PRODUCT_VIEW} from "./constants/routes"; import {PRODUCT_ADD, PRODUCT_LIST, PRODUCT_VIEW, REGISTER} from "./constants/routes";
import Layout from "./components/Layout/Layout"; import Layout from "./components/Layout/Layout";
import Products from "./containers/Products/Products"; import Products from "./containers/Products/Products";
import AddProduct from "./containers/Addproduct/AddProduct"; import AddProduct from "./containers/Addproduct/AddProduct";
import Register from "./containers/Auth/Register/Register";
const App = () => ( const App = () => (
<> <>
<Routes> <Routes>
<Route element={<Layout />}> <Route element={<Layout />}>
<Route index element={<Products />}/> <Route index element={<Products />}/>
<Route path={REGISTER} element={<Register />}/>
<Route path={PRODUCT_LIST} element={<Products />}/> <Route path={PRODUCT_LIST} element={<Products />}/>
<Route path={PRODUCT_ADD} element={<AddProduct />}/> <Route path={PRODUCT_ADD} element={<AddProduct />}/>
<Route path={PRODUCT_VIEW} element={<h1>Show product</h1>}/> <Route path={PRODUCT_VIEW} element={<h1>Show product</h1>}/>
......
import {AppBar, Box, Toolbar, Typography, Button, IconButton} from '@mui/material'; import {AppBar, Box, Toolbar, Typography, Button, IconButton} from '@mui/material';
import MenuIcon from '@mui/icons-material/Menu'; import MenuIcon from '@mui/icons-material/Menu';
import {NavLink} from "react-router-dom"; import {NavLink} from "react-router-dom";
import {MAIN, PRODUCT_ADD} from "../../../constants/routes"; import {MAIN, REGISTER} from "../../../constants/routes";
const AppToolbar = () => { const AppToolbar = () => {
return ( return (
...@@ -21,7 +21,7 @@ const AppToolbar = () => { ...@@ -21,7 +21,7 @@ const AppToolbar = () => {
Shop Shop
</Typography> </Typography>
<Button color="inherit" component={NavLink} to={MAIN}>Home</Button> <Button color="inherit" component={NavLink} to={MAIN}>Home</Button>
<Button color="inherit" component={NavLink} to={PRODUCT_ADD}>Add product</Button> <Button color="inherit" component={NavLink} to={REGISTER}>Sign up</Button>
</Toolbar> </Toolbar>
</AppBar> </AppBar>
</Box> </Box>
......
...@@ -2,3 +2,5 @@ export const MAIN = '/'; ...@@ -2,3 +2,5 @@ export const MAIN = '/';
export const PRODUCT_ADD = '/products/add'; export const PRODUCT_ADD = '/products/add';
export const PRODUCT_LIST = '/products/'; export const PRODUCT_LIST = '/products/';
export const PRODUCT_VIEW = '/products/:id'; export const PRODUCT_VIEW = '/products/:id';
export const REGISTER = '/register';
export const LOGIN = '/login';
import {useState} from "react";
import {Avatar, Button, Container, Grid, TextField, Typography, Link, Box, CssBaseline} from "@mui/material";
import { createTheme, ThemeProvider } from '@mui/material/styles';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import {Link as RouterLink, useNavigate} from 'react-router-dom';
import {LOGIN} from "../../../constants/routes";
import {useDispatch, useSelector} from "react-redux";
import {registerUser} from "../../../store/actions/usersActions";
const theme = createTheme();
const Register = () => {
const error = useSelector(({usersState}) => usersState.error);
const dispatch = useDispatch();
const navigate = useNavigate();
const [state, setState] = useState({
username: "",
password: ""
});
const inputChangeHandler = (e) => {
const {name, value} = e.currentTarget;
setState(prevState => {
return {...prevState, [name]: value};
});
};
const handleSubmit = (e) => {
e.preventDefault();
dispatch(registerUser({
data: {...state},
callback: () => navigate('/')
}));
};
const getFieldError = (field) => {
return error?.errors[field]?.message;
};
return (
<ThemeProvider theme={theme}>
<Container component="main" maxWidth="xs">
<CssBaseline />
<Box
sx={{
marginTop: 8,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign up
</Typography>
<Box component="form" noValidate onSubmit={handleSubmit} sx={{ mt: 3 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
required
fullWidth
id="username"
label="Username"
name="username"
autoComplete="family-name"
onChange={inputChangeHandler}
value={state.username}
error={!!getFieldError('username')}
helperText={getFieldError('username')}
/>
</Grid>
<Grid item xs={12}>
<TextField
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="new-password"
onChange={inputChangeHandler}
value={state.password}
error={!!getFieldError('password')}
helperText={getFieldError('password')}
/>
</Grid>
</Grid>
<Button
type="submit"
fullWidth
variant="contained"
sx={{ mt: 3, mb: 2 }}
>
Sign Up
</Button>
<Grid container justifyContent="flex-end">
<Grid item>
<Link href="#" variant="body2" component={RouterLink} to={LOGIN}>
Already have an account? Sign in
</Link>
</Grid>
</Grid>
</Box>
</Box>
</Container>
</ThemeProvider>
);
};
export default Register;
...@@ -8,7 +8,7 @@ import ProductList from "../../components/Product/ProductList/ProductList"; ...@@ -8,7 +8,7 @@ import ProductList from "../../components/Product/ProductList/ProductList";
const Products = () => { const Products = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const products = useSelector(state => state.products); const products = useSelector(({productsState}) => productsState.products);
useEffect(() => { useEffect(() => {
dispatch(fetchProducts()); dispatch(fetchProducts());
......
...@@ -5,10 +5,16 @@ import App from './App'; ...@@ -5,10 +5,16 @@ import App from './App';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
import {Provider} from "react-redux"; import {Provider} from "react-redux";
import {configureStore} from "@reduxjs/toolkit"; import {configureStore} from "@reduxjs/toolkit";
import reducer from './store/services/productsSlice'; import productsReducer from './store/services/productsSlice';
import usersReducer from './store/services/usersSlice';
import {BrowserRouter} from "react-router-dom"; import {BrowserRouter} from "react-router-dom";
const store = configureStore({reducer}); const store = configureStore({
reducer: {
usersState: usersReducer,
productsState: productsReducer
}
});
const root = ReactDOM.createRoot(document.getElementById('root')); const root = ReactDOM.createRoot(document.getElementById('root'));
root.render( root.render(
......
import {createAsyncThunk} from "@reduxjs/toolkit";
import axiosApi from "../../api/axiosApi";
import {setError} from "../services/usersSlice";
export const registerUser = createAsyncThunk(
'users/register',
async (payload, {dispatch}) => await axiosApi
.post('/users', payload.data)
.then(res => payload.callback())
.catch(e => {
if (e?.response?.data) dispatch(setError(e.response.data));
throw e;
})
);
import {createSlice} from "@reduxjs/toolkit";
import {registerUser} from "../actions/usersActions";
const initialState = {
error: null,
loading: false
};
const usersSlice = createSlice({
name: 'users',
initialState,
reducers: {
setError: (state, action) => {
state.error = action.payload;
}
},
extraReducers: builder => {
builder
.addCase(
registerUser.pending,
state => {
state.error = null;
state.loading = true;
}
)
.addCase(
registerUser.rejected,
state => {
state.loading = false;
}
)
.addCase(
registerUser.fulfilled,
state => {
state.loading = false;
}
);
}
});
export const {setError} = usersSlice.actions;
export default usersSlice.reducer;
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