Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
A
ajs12_shop
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Egor Kremnev
ajs12_shop
Commits
2584765b
Commit
2584765b
authored
May 18, 2023
by
Egor Kremnev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add role authorization. fix add product form. refactor routes
parent
ddb7b99c
Show whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
311 additions
and
166 deletions
+311
-166
config.js
api_client/config.js
+1
-1
fixtures.js
api_client/fixtures.js
+4
-2
auth.js
api_client/middleware/auth.js
+4
-2
permit.js
api_client/middleware/permit.js
+15
-0
User.js
api_client/models/User.js
+6
-0
categories.js
api_client/routes/categories.js
+3
-2
products.js
api_client/routes/products.js
+3
-2
users.js
api_client/routes/users.js
+11
-15
App.js
front_client/src/App.js
+2
-25
Routes.jsx
front_client/src/Routes.jsx
+37
-0
axiosApi.js
front_client/src/api/axiosApi.js
+0
-10
ProductForm.jsx
...client/src/components/Product/ProductForm/ProductForm.jsx
+42
-45
ProtectedRoute.jsx
...t_client/src/components/ProtectedRoute/ProtectedRoute.jsx
+0
-11
FormElement.jsx
...client/src/components/UI/Form/FormElement/FormElement.jsx
+63
-0
AddProduct.jsx
front_client/src/containers/AddProduct/AddProduct.jsx
+12
-2
Login.jsx
front_client/src/containers/Auth/Login/Login.jsx
+3
-3
Register.jsx
front_client/src/containers/Auth/Register/Register.jsx
+3
-3
Products.jsx
front_client/src/containers/Products/Products.jsx
+8
-5
index.js
front_client/src/index.js
+4
-36
setupInterceptors.js
front_client/src/services/setupInterceptors.js
+18
-0
categoriesActions.js
front_client/src/store/actions/categoriesActions.js
+7
-0
usersActions.js
front_client/src/store/actions/usersActions.js
+3
-2
configureStore.js
front_client/src/store/configureStore.js
+40
-0
categoriesSlice.js
front_client/src/store/services/categoriesSlice.js
+22
-0
No files found.
api_client/config.js
View file @
2584765b
...
@@ -6,7 +6,7 @@ module.exports = {
...
@@ -6,7 +6,7 @@ module.exports = {
port
:
8001
,
port
:
8001
,
uploadPath
:
path
.
join
(
rootPath
,
'public'
,
'uploads'
),
uploadPath
:
path
.
join
(
rootPath
,
'public'
,
'uploads'
),
db
:
{
db
:
{
host
:
'mongodb://
localhost
'
,
host
:
'mongodb://
127.0.0.1
'
,
database
:
'shop'
,
database
:
'shop'
,
}
}
};
};
api_client/fixtures.js
View file @
2584765b
...
@@ -54,12 +54,14 @@ db.once('open', async () => {
...
@@ -54,12 +54,14 @@ db.once('open', async () => {
{
{
username
:
"user"
,
username
:
"user"
,
password
:
"qwerty"
,
password
:
"qwerty"
,
token
:
null
token
:
null
,
role
:
'user'
},
},
{
{
username
:
"admin"
,
username
:
"admin"
,
password
:
"qwerty"
,
password
:
"qwerty"
,
token
:
null
token
:
null
,
role
:
'admin'
}
}
]);
]);
...
...
api_client/middleware/
secureRoute
.js
→
api_client/middleware/
auth
.js
View file @
2584765b
const
User
=
require
(
"../models/User"
);
const
User
=
require
(
"../models/User"
);
const
secureRoute
=
async
(
req
,
res
,
next
)
=>
{
const
auth
=
async
(
req
,
res
,
next
)
=>
{
const
token
=
req
.
get
(
'Authorization'
);
const
token
=
req
.
get
(
'Authorization'
);
if
(
!
token
)
return
res
if
(
!
token
)
return
res
...
@@ -13,7 +13,9 @@ const secureRoute = async (req, res, next) => {
...
@@ -13,7 +13,9 @@ const secureRoute = async (req, res, next) => {
.
status
(
401
)
.
status
(
401
)
.
send
(
'Token is wrong'
);
.
send
(
'Token is wrong'
);
req
.
user
=
user
;
next
();
next
();
}
}
module
.
exports
=
secureRoute
;
module
.
exports
=
auth
;
api_client/middleware/permit.js
0 → 100644
View file @
2584765b
const
permit
=
(...
roles
)
=>
{
return
(
req
,
res
,
next
)
=>
{
if
(
!
req
.
user
)
{
return
res
.
status
(
401
).
send
({
message
:
'Unauthenticated'
});
}
if
(
!
roles
.
includes
(
req
.
user
.
role
))
{
return
res
.
status
(
403
).
send
({
message
:
'Unauthorized'
});
}
next
();
};
};
module
.
exports
=
permit
;
api_client/models/User.js
View file @
2584765b
...
@@ -33,6 +33,12 @@ const UserSchema = new Schema({
...
@@ -33,6 +33,12 @@ const UserSchema = new Schema({
},
},
message
:
"Token duplicated"
message
:
"Token duplicated"
}
}
},
role
:
{
type
:
String
,
required
:
true
,
default
:
'user'
,
enum
:
[
'user'
,
'admin'
]
}
}
});
});
...
...
api_client/routes/categories.js
View file @
2584765b
const
router
=
require
(
'express'
).
Router
();
const
router
=
require
(
'express'
).
Router
();
const
Category
=
require
(
'../models/Category'
);
const
Category
=
require
(
'../models/Category'
);
const
secureRoute
=
require
(
"../middleware/secureRoute"
);
const
auth
=
require
(
"../middleware/auth"
);
const
permit
=
require
(
"../middleware/permit"
);
router
.
get
(
'/'
,
async
(
req
,
res
)
=>
{
router
.
get
(
'/'
,
async
(
req
,
res
)
=>
{
try
{
try
{
...
@@ -10,7 +11,7 @@ router.get('/', async (req, res) => {
...
@@ -10,7 +11,7 @@ router.get('/', async (req, res) => {
}
}
});
});
router
.
post
(
'/'
,
secureRoute
,
async
(
req
,
res
)
=>
{
router
.
post
(
'/'
,
[
auth
,
permit
(
'admin'
)]
,
async
(
req
,
res
)
=>
{
const
category
=
new
Category
(
req
.
body
);
const
category
=
new
Category
(
req
.
body
);
try
{
try
{
...
...
api_client/routes/products.js
View file @
2584765b
...
@@ -5,7 +5,8 @@ const multer = require('multer');
...
@@ -5,7 +5,8 @@ const multer = require('multer');
const
path
=
require
(
'path'
);
const
path
=
require
(
'path'
);
const
{
uploadPath
}
=
require
(
'./../config'
);
const
{
uploadPath
}
=
require
(
'./../config'
);
const
Product
=
require
(
'../models/Product'
);
const
Product
=
require
(
'../models/Product'
);
const
secureRoute
=
require
(
"../middleware/secureRoute"
);
const
auth
=
require
(
"../middleware/auth"
);
const
permit
=
require
(
"../middleware/permit"
);
const
storage
=
multer
.
diskStorage
({
const
storage
=
multer
.
diskStorage
({
destination
:
(
req
,
file
,
cb
)
=>
{
destination
:
(
req
,
file
,
cb
)
=>
{
...
@@ -19,7 +20,7 @@ const storage = multer.diskStorage({
...
@@ -19,7 +20,7 @@ const storage = multer.diskStorage({
const
upload
=
multer
({
storage
});
const
upload
=
multer
({
storage
});
const
createRoutes
=
()
=>
{
const
createRoutes
=
()
=>
{
router
.
post
(
'/'
,
upload
.
single
(
'image'
),
secureRoute
,
async
(
req
,
res
)
=>
{
router
.
post
(
'/'
,
[
auth
,
permit
(
'admin'
,
'manager'
),
upload
.
single
(
'image'
)]
,
async
(
req
,
res
)
=>
{
const
productData
=
{...
req
.
body
};
const
productData
=
{...
req
.
body
};
if
(
req
.
file
)
productData
.
image
=
req
.
file
.
filename
;
if
(
req
.
file
)
productData
.
image
=
req
.
file
.
filename
;
...
...
api_client/routes/users.js
View file @
2584765b
const
router
=
require
(
'express'
).
Router
();
const
router
=
require
(
'express'
).
Router
();
const
User
=
require
(
'../models/User'
);
const
User
=
require
(
'../models/User'
);
const
secureRoute
=
require
(
"../middleware/secureRoute
"
);
const
auth
=
require
(
"../middleware/auth
"
);
router
.
post
(
'/'
,
async
(
req
,
res
)
=>
{
router
.
post
(
'/'
,
async
(
req
,
res
)
=>
{
try
{
try
{
const
user
=
new
User
(
req
.
body
);
const
user
=
new
User
({
username
:
req
.
body
.
username
,
password
:
req
.
body
.
password
});
user
.
generateToken
();
user
.
generateToken
();
await
user
.
save
();
await
user
.
save
();
...
@@ -38,25 +41,18 @@ router.post('/login', async (req, res) => {
...
@@ -38,25 +41,18 @@ router.post('/login', async (req, res) => {
}
}
});
});
router
.
get
(
'/profile'
,
secureRoute
,
async
(
req
,
res
)
=>
{
router
.
get
(
'/profile'
,
auth
,
async
(
req
,
res
)
=>
{
const
token
=
req
.
get
(
'Authorization'
);
const
user
=
await
User
.
findOne
({
token
});
res
.
send
({
res
.
send
({
message
:
"Большой большой сикрет"
,
message
:
"Большой большой сикрет"
,
username
:
user
.
username
username
:
req
.
user
.
username
});
});
});
});
router
.
delete
(
'/logout'
,
secureRoute
,
async
(
req
,
res
)
=>
{
router
.
delete
(
'/logout'
,
auth
,
async
(
req
,
res
)
=>
{
const
token
=
req
.
get
(
'Authorization'
);
req
.
user
.
token
=
null
;
const
success
=
{
message
:
'Success'
};
req
.
user
.
save
();
const
user
=
await
User
.
findOne
({
token
});
user
.
token
=
null
;
user
.
save
();
res
.
send
(
success
);
res
.
send
(
{
message
:
'Success'
}
);
});
});
module
.
exports
=
router
;
module
.
exports
=
router
;
front_client/src/App.js
View file @
2584765b
import
{
Route
,
Routes
}
from
"react-router-dom"
;
import
{
LOGIN
,
PRODUCT_ADD
,
PRODUCT_LIST
,
PRODUCT_VIEW
,
REGISTER
}
from
"./constants/routes"
;
import
Layout
from
"./components/Layout/Layout"
;
import
Products
from
"./containers/Products/Products"
;
import
AddProduct
from
"./containers/AddProduct/AddProduct"
;
import
Register
from
"./containers/Auth/Register/Register"
;
import
Login
from
"./containers/Auth/Login/Login"
;
import
{
useSelector
}
from
"react-redux"
;
import
{
useSelector
}
from
"react-redux"
;
import
ProtectedRoute
from
"./components/ProtectedRoute/ProtectedRoute
"
;
import
Routes
from
"./Routes
"
;
const
App
=
()
=>
{
const
App
=
()
=>
{
const
user
=
useSelector
(({
usersState
})
=>
usersState
.
user
);
const
user
=
useSelector
(({
usersState
})
=>
usersState
.
user
);
const
productAdd
=
<
ProtectedRoute
return
<
Routes
user
=
{
user
}
/>
;
isAllowed
=
{
!!
user
}
redirectPath
=
{
LOGIN
}
>
<
AddProduct
/>
<
/ProtectedRoute>
;
return
<
Routes
>
<
Route
element
=
{
<
Layout
/>
}
>
<
Route
index
element
=
{
<
Products
/>
}
/
>
<
Route
path
=
{
REGISTER
}
element
=
{
<
Register
/>
}
/
>
<
Route
path
=
{
LOGIN
}
element
=
{
<
Login
/>
}
/
>
<
Route
path
=
{
PRODUCT_LIST
}
element
=
{
<
Products
/>
}
/
>
<
Route
path
=
{
PRODUCT_ADD
}
element
=
{
productAdd
}
/
>
<
Route
path
=
{
PRODUCT_VIEW
}
element
=
{
<
h1
>
Show
product
<
/h1>}/
>
<
/Route
>
<
/Routes>
;
};
};
...
...
front_client/src/Routes.jsx
0 → 100644
View file @
2584765b
import
{
Navigate
,
Outlet
,
Route
,
Routes
as
RoutesSwitch
}
from
"react-router-dom"
;
import
{
LOGIN
,
MAIN
,
PRODUCT_ADD
,
PRODUCT_LIST
,
PRODUCT_VIEW
,
REGISTER
}
from
"./constants/routes"
;
import
AddProduct
from
"./containers/AddProduct/AddProduct"
;
import
Layout
from
"./components/Layout/Layout"
;
import
Products
from
"./containers/Products/Products"
;
import
Register
from
"./containers/Auth/Register/Register"
;
import
Login
from
"./containers/Auth/Login/Login"
;
const
ProtectedRoute
=
({
isAllowed
,
redirectPath
,
children
})
=>
{
if
(
!
isAllowed
)
{
return
<
Navigate
to=
{
redirectPath
}
replace
/>;
}
return
children
||
<
Outlet
/>;
};
const
Routes
=
({
user
})
=>
{
const
productAdd
=
<
ProtectedRoute
isAllowed=
{
!!
user
&&
user
.
role
===
'admin'
}
redirectPath=
{
!!
user
?
MAIN
:
LOGIN
}
>
<
AddProduct
/>
</
ProtectedRoute
>;
return
<
RoutesSwitch
>
<
Route
element=
{
<
Layout
/>
}
>
<
Route
index
element=
{
<
Products
/>
}
/>
<
Route
path=
{
REGISTER
}
element=
{
<
Register
/>
}
/>
<
Route
path=
{
LOGIN
}
element=
{
<
Login
/>
}
/>
<
Route
path=
{
PRODUCT_LIST
}
element=
{
<
Products
/>
}
/>
<
Route
path=
{
PRODUCT_ADD
}
element=
{
productAdd
}
/>
<
Route
path=
{
PRODUCT_VIEW
}
element=
{
<
h1
>
Show product
</
h1
>
}
/>
</
Route
>
</
RoutesSwitch
>;
};
export
default
Routes
;
front_client/src/api/axiosApi.js
View file @
2584765b
...
@@ -5,14 +5,4 @@ const instance = axios.create({
...
@@ -5,14 +5,4 @@ const instance = axios.create({
baseURL
:
apiUrl
+
"/api/v1"
baseURL
:
apiUrl
+
"/api/v1"
});
});
instance
.
interceptors
.
request
.
use
(
function
(
config
)
{
if
(
localStorage
.
getItem
(
'user'
)
!==
null
)
{
config
.
headers
.
Authorization
=
JSON
.
parse
(
localStorage
.
getItem
(
'user'
)).
token
;
}
return
config
;
},
function
(
error
)
{
return
Promise
.
reject
(
error
);
});
export
default
instance
;
export
default
instance
;
front_client/src/components/Product/ProductForm/ProductForm.jsx
View file @
2584765b
import
{
useState
}
from
"react"
;
import
{
useState
}
from
"react"
;
import
{
Button
,
Grid
,
TextField
}
from
"@mui/material"
;
import
{
Button
,
Grid
}
from
"@mui/material"
;
import
FileInput
from
"../../UI/Form/FileInput/FileInput"
;
import
FileInput
from
"../../UI/Form/FileInput/FileInput"
;
import
FormElement
from
"../../UI/Form/FormElement/FormElement"
;
const
ProductForm
=
({
createProductHandler
})
=>
{
const
ProductForm
=
({
createProductHandler
,
categories
})
=>
{
const
[
state
,
setState
]
=
useState
({
const
[
state
,
setState
]
=
useState
({
title
:
""
,
title
:
""
,
price
:
""
,
price
:
""
,
description
:
""
,
description
:
""
,
image
:
''
category
:
""
,
image
:
""
});
});
const
submitFormHandler
=
e
=>
{
const
submitFormHandler
=
e
=>
{
...
@@ -46,48 +48,43 @@ const ProductForm = ({createProductHandler}) => {
...
@@ -46,48 +48,43 @@ const ProductForm = ({createProductHandler}) => {
onSubmit=
{
submitFormHandler
}
onSubmit=
{
submitFormHandler
}
>
>
<
Grid
container
direction=
"column"
spacing=
{
2
}
>
<
Grid
container
direction=
"column"
spacing=
{
2
}
>
<
Grid
item
xs
>
<
FormElement
<
TextField
fullWidth
variant=
"outlined"
id=
"title"
id=
"title"
label=
"Title"
label=
"Title"
value=
{
state
.
title
}
value=
{
state
.
title
}
onChange=
{
inputChangeHandler
}
onChange=
{
inputChangeHandler
}
name=
"title"
name=
"title"
/>
/>
</
Grid
>
<
FormElement
<
Grid
item
xs
>
<
TextField
fullWidth
variant=
"outlined"
id=
"price"
id=
"price"
label=
"Price"
label=
"Price"
value=
{
state
.
price
}
value=
{
state
.
price
}
onChange=
{
inputChangeHandler
}
onChange=
{
inputChangeHandler
}
name=
"price"
name=
"price"
/>
/>
</
Grid
>
<
FormElement
<
Grid
item
xs
>
multiline=
{
true
}
<
TextField
fullWidth
multiline
rows=
{
3
}
rows=
{
3
}
variant=
"outlined"
id=
"description"
id=
"description"
label=
"Description"
label=
"Description"
value=
{
state
.
description
}
value=
{
state
.
description
}
onChange=
{
inputChangeHandler
}
onChange=
{
inputChangeHandler
}
name=
"description"
name=
"description"
/>
/>
</
Grid
>
<
FormElement
<
Grid
item
xs
>
id=
"category"
label=
"Category"
value=
{
state
.
category
}
onChange=
{
inputChangeHandler
}
name=
"category"
select=
{
true
}
options=
{
categories
}
/>
<
FileInput
<
FileInput
onChange=
{
onFileChangeHandler
}
onChange=
{
onFileChangeHandler
}
name=
"image"
name=
"image"
label=
"Image"
label=
"Image"
/>
/>
</
Grid
>
<
Grid
item
xs
>
<
Grid
item
xs
>
<
Button
type=
"submit"
color=
"primary"
variant=
"contained"
>
Create
</
Button
>
<
Button
type=
"submit"
color=
"primary"
variant=
"contained"
>
Create
</
Button
>
</
Grid
>
</
Grid
>
...
...
front_client/src/components/ProtectedRoute/ProtectedRoute.jsx
deleted
100644 → 0
View file @
ddb7b99c
import
{
Navigate
,
Outlet
}
from
"react-router-dom"
;
const
ProtectedRoute
=
({
isAllowed
,
redirectPath
,
children
})
=>
{
if
(
!
isAllowed
)
{
return
<
Navigate
to=
{
redirectPath
}
replace
/>;
}
return
children
||
<
Outlet
/>;
};
export
default
ProtectedRoute
;
front_client/src/components/UI/Form/FormElement/FomElement.jsx
→
front_client/src/components/UI/Form/FormElement/Fo
r
mElement.jsx
View file @
2584765b
import
{
Grid
,
TextField
}
from
"@mui/material"
;
import
{
Grid
,
MenuItem
,
TextField
}
from
"@mui/material"
;
import
PropTypes
from
"prop-types"
;
import
PropTypes
from
"prop-types"
;
const
FomElement
=
({
name
,
label
,
value
,
onChange
,
required
,
error
,
type
})
=>
{
const
FormElement
=
({
name
,
label
,
value
,
onChange
,
required
,
error
,
type
,
select
,
multiline
,
rows
,
options
})
=>
{
let
inputChildren
=
null
;
if
(
select
)
{
inputChildren
=
options
.
map
(
option
=>
(
<
MenuItem
key=
{
option
.
_id
}
value=
{
option
.
_id
}
>
{
option
.
title
}
</
MenuItem
>
));
}
return
<
Grid
item
xs=
{
12
}
>
return
<
Grid
item
xs=
{
12
}
>
<
TextField
<
TextField
fullWidth
fullWidth
...
@@ -15,18 +37,27 @@ const FomElement = ({name, label, value, onChange, required, error, type}) => {
...
@@ -15,18 +37,27 @@ const FomElement = ({name, label, value, onChange, required, error, type}) => {
onChange=
{
onChange
}
onChange=
{
onChange
}
autoComplete=
{
name
}
autoComplete=
{
name
}
type=
{
type
}
type=
{
type
}
/>
multiline=
{
multiline
}
rows=
{
rows
}
select=
{
select
}
>
{
inputChildren
}
</
TextField
>
</
Grid
>;
</
Grid
>;
};
};
FomElement
.
propTypes
=
{
Fo
r
mElement
.
propTypes
=
{
name
:
PropTypes
.
string
.
isRequired
,
name
:
PropTypes
.
string
.
isRequired
,
label
:
PropTypes
.
string
.
isRequired
,
label
:
PropTypes
.
string
.
isRequired
,
value
:
PropTypes
.
string
.
isRequired
,
value
:
PropTypes
.
string
.
isRequired
,
type
:
PropTypes
.
string
,
type
:
PropTypes
.
string
,
error
:
PropTypes
.
string
,
error
:
PropTypes
.
string
,
required
:
PropTypes
.
bool
,
required
:
PropTypes
.
bool
,
onChange
:
PropTypes
.
func
.
isRequired
onChange
:
PropTypes
.
func
.
isRequired
,
select
:
PropTypes
.
bool
,
multiline
:
PropTypes
.
bool
,
rows
:
PropTypes
.
number
,
options
:
PropTypes
.
arrayOf
(
PropTypes
.
object
)
};
};
export
default
FomElement
;
export
default
Fo
r
mElement
;
front_client/src/containers/AddProduct/AddProduct.jsx
View file @
2584765b
import
ProductForm
from
"../../components/Product/ProductForm/ProductForm"
;
import
ProductForm
from
"../../components/Product/ProductForm/ProductForm"
;
import
{
Typography
}
from
"@mui/material"
;
import
{
Typography
}
from
"@mui/material"
;
import
{
createProduct
}
from
"../../store/actions/productsActions"
;
import
{
createProduct
}
from
"../../store/actions/productsActions"
;
import
{
useDispatch
}
from
"react-redux"
;
import
{
useDispatch
,
useSelector
}
from
"react-redux"
;
import
{
useNavigate
}
from
"react-router-dom"
;
import
{
useNavigate
}
from
"react-router-dom"
;
import
{
fetchCategories
}
from
"../../store/actions/categoriesActions"
;
import
{
useEffect
}
from
"react"
;
const
AddProduct
=
()
=>
{
const
AddProduct
=
()
=>
{
const
dispatch
=
useDispatch
();
const
dispatch
=
useDispatch
();
const
navigate
=
useNavigate
();
const
navigate
=
useNavigate
();
const
categories
=
useSelector
(({
categoriesState
})
=>
categoriesState
.
categories
);
useEffect
(()
=>
{
dispatch
(
fetchCategories
());
},
[]);
const
onProductFormSubmit
=
async
data
=>
{
const
onProductFormSubmit
=
async
data
=>
{
await
dispatch
(
createProduct
({
data
,
callback
:
()
=>
navigate
(
'/'
)}));
await
dispatch
(
createProduct
({
data
,
callback
:
()
=>
navigate
(
'/'
)}));
...
@@ -18,7 +25,10 @@ const AddProduct = () => {
...
@@ -18,7 +25,10 @@ const AddProduct = () => {
<
Typography
variant=
'h4'
>
<
Typography
variant=
'h4'
>
New Product
New Product
</
Typography
>
</
Typography
>
<
ProductForm
createProductHandler=
{
onProductFormSubmit
}
/>
<
ProductForm
createProductHandler=
{
onProductFormSubmit
}
categories=
{
categories
}
/>
</>
</>
);
);
};
};
...
...
front_client/src/containers/Auth/Login/Login.jsx
View file @
2584765b
...
@@ -6,7 +6,7 @@ import {Link as RouterLink, useLocation, useNavigate} from 'react-router-dom';
...
@@ -6,7 +6,7 @@ import {Link as RouterLink, useLocation, useNavigate} from 'react-router-dom';
import
{
REGISTER
}
from
"../../../constants/routes"
;
import
{
REGISTER
}
from
"../../../constants/routes"
;
import
{
useDispatch
,
useSelector
}
from
"react-redux"
;
import
{
useDispatch
,
useSelector
}
from
"react-redux"
;
import
{
loginUser
}
from
"../../../store/actions/usersActions"
;
import
{
loginUser
}
from
"../../../store/actions/usersActions"
;
import
Fo
mElement
from
"../../../components/UI/Form/FormElement/Fo
mElement"
;
import
Fo
rmElement
from
"../../../components/UI/Form/FormElement/For
mElement"
;
import
{
setLoginError
}
from
"../../../store/services/usersSlice"
;
import
{
setLoginError
}
from
"../../../store/services/usersSlice"
;
const
theme
=
createTheme
();
const
theme
=
createTheme
();
...
@@ -63,14 +63,14 @@ const Login = () => {
...
@@ -63,14 +63,14 @@ const Login = () => {
{
error
&&
<
Alert
severity=
"error"
>
{
error
.
error
}
</
Alert
>
}
{
error
&&
<
Alert
severity=
"error"
>
{
error
.
error
}
</
Alert
>
}
<
Box
component=
"form"
noValidate
onSubmit=
{
handleSubmit
}
sx=
{
{
mt
:
3
}
}
>
<
Box
component=
"form"
noValidate
onSubmit=
{
handleSubmit
}
sx=
{
{
mt
:
3
}
}
>
<
Grid
container
spacing=
{
2
}
>
<
Grid
container
spacing=
{
2
}
>
<
FomElement
<
Fo
r
mElement
required=
{
true
}
required=
{
true
}
label=
"Username"
label=
"Username"
name=
"username"
name=
"username"
onChange=
{
inputChangeHandler
}
onChange=
{
inputChangeHandler
}
value=
{
state
.
username
}
value=
{
state
.
username
}
/>
/>
<
FomElement
<
Fo
r
mElement
required=
{
true
}
required=
{
true
}
name=
"password"
name=
"password"
label=
"Password"
label=
"Password"
...
...
front_client/src/containers/Auth/Register/Register.jsx
View file @
2584765b
...
@@ -6,7 +6,7 @@ import {Link as RouterLink, useLocation, useNavigate} from 'react-router-dom';
...
@@ -6,7 +6,7 @@ import {Link as RouterLink, useLocation, useNavigate} from 'react-router-dom';
import
{
LOGIN
}
from
"../../../constants/routes"
;
import
{
LOGIN
}
from
"../../../constants/routes"
;
import
{
useDispatch
,
useSelector
}
from
"react-redux"
;
import
{
useDispatch
,
useSelector
}
from
"react-redux"
;
import
{
registerUser
}
from
"../../../store/actions/usersActions"
;
import
{
registerUser
}
from
"../../../store/actions/usersActions"
;
import
Fo
mElement
from
"../../../components/UI/Form/FormElement/Fo
mElement"
;
import
Fo
rmElement
from
"../../../components/UI/Form/FormElement/For
mElement"
;
import
{
setRegisterError
}
from
"../../../store/services/usersSlice"
;
import
{
setRegisterError
}
from
"../../../store/services/usersSlice"
;
const
theme
=
createTheme
();
const
theme
=
createTheme
();
...
@@ -66,7 +66,7 @@ const Register = () => {
...
@@ -66,7 +66,7 @@ const Register = () => {
</
Typography
>
</
Typography
>
<
Box
component=
"form"
noValidate
onSubmit=
{
handleSubmit
}
sx=
{
{
mt
:
3
}
}
>
<
Box
component=
"form"
noValidate
onSubmit=
{
handleSubmit
}
sx=
{
{
mt
:
3
}
}
>
<
Grid
container
spacing=
{
2
}
>
<
Grid
container
spacing=
{
2
}
>
<
FomElement
<
Fo
r
mElement
required=
{
true
}
required=
{
true
}
label=
"Username"
label=
"Username"
name=
"username"
name=
"username"
...
@@ -74,7 +74,7 @@ const Register = () => {
...
@@ -74,7 +74,7 @@ const Register = () => {
value=
{
state
.
username
}
value=
{
state
.
username
}
error=
{
getFieldError
(
'username'
)
}
error=
{
getFieldError
(
'username'
)
}
/>
/>
<
FomElement
<
Fo
r
mElement
required=
{
true
}
required=
{
true
}
name=
"password"
name=
"password"
label=
"Password"
label=
"Password"
...
...
front_client/src/containers/Products/Products.jsx
View file @
2584765b
...
@@ -9,6 +9,7 @@ import ProductList from "../../components/Product/ProductList/ProductList";
...
@@ -9,6 +9,7 @@ import ProductList from "../../components/Product/ProductList/ProductList";
const
Products
=
()
=>
{
const
Products
=
()
=>
{
const
dispatch
=
useDispatch
();
const
dispatch
=
useDispatch
();
const
products
=
useSelector
(({
productsState
})
=>
productsState
.
products
);
const
products
=
useSelector
(({
productsState
})
=>
productsState
.
products
);
const
user
=
useSelector
(({
usersState
})
=>
usersState
.
user
);
useEffect
(()
=>
{
useEffect
(()
=>
{
dispatch
(
fetchProducts
());
dispatch
(
fetchProducts
());
...
@@ -28,11 +29,13 @@ const Products = () => {
...
@@ -28,11 +29,13 @@ const Products = () => {
Products list
Products list
</
Typography
>
</
Typography
>
</
Grid
>
</
Grid
>
<
Grid
item
>
{
user
&&
user
.
role
===
'admin'
&&
<
Grid
item
>
<
Button
color=
"primary"
component=
{
Link
}
to=
{
PRODUCT_ADD
}
>
<
Button
color=
"primary"
component=
{
Link
}
to=
{
PRODUCT_ADD
}
>
Add product
Add product
</
Button
>
</
Button
>
</
Grid
>
</
Grid
>
}
</
Grid
>
</
Grid
>
<
ProductList
products=
{
products
}
/>
<
ProductList
products=
{
products
}
/>
</
Grid
>
</
Grid
>
...
...
front_client/src/index.js
View file @
2584765b
...
@@ -4,43 +4,9 @@ import './index.css';
...
@@ -4,43 +4,9 @@ import './index.css';
import
App
from
'./App'
;
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
productsReducer
from
'./store/services/productsSlice'
;
import
usersReducer
from
'./store/services/usersSlice'
;
import
{
BrowserRouter
}
from
"react-router-dom"
;
import
{
BrowserRouter
}
from
"react-router-dom"
;
import
store
from
'./store/configureStore'
;
const
localStorageMiddleware
=
({
getState
})
=>
next
=>
action
=>
{
import
setup
from
"./services/setupInterceptors"
;
const
result
=
next
(
action
);
if
(
getState
().
usersState
.
user
)
{
localStorage
.
setItem
(
'user'
,
JSON
.
stringify
(
getState
().
usersState
.
user
));
}
else
{
localStorage
.
removeItem
(
'user'
);
}
return
result
;
};
const
reHydrateStore
=
()
=>
{
const
userLocalStorage
=
localStorage
.
getItem
(
'user'
);
if
(
userLocalStorage
!==
null
||
userLocalStorage
!==
'null'
||
userLocalStorage
!==
''
)
{
return
{
usersState
:
{
user
:
JSON
.
parse
(
localStorage
.
getItem
(
'user'
))
}
};
}
return
undefined
;
};
const
store
=
configureStore
({
reducer
:
{
usersState
:
usersReducer
,
productsState
:
productsReducer
},
preloadedState
:
reHydrateStore
(),
middleware
:
getDefaultMiddleware
=>
getDefaultMiddleware
().
concat
(
localStorageMiddleware
)
});
const
root
=
ReactDOM
.
createRoot
(
document
.
getElementById
(
'root'
));
const
root
=
ReactDOM
.
createRoot
(
document
.
getElementById
(
'root'
));
root
.
render
(
root
.
render
(
...
@@ -51,6 +17,8 @@ root.render(
...
@@ -51,6 +17,8 @@ root.render(
<
/Provider
>
<
/Provider
>
);
);
setup
(
store
);
// If you want to start measuring performance in your app, pass a function
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
...
...
front_client/src/services/setupInterceptors.js
0 → 100644
View file @
2584765b
import
axiosApi
from
"../api/axiosApi"
;
const
setup
=
({
getState
})
=>
{
axiosApi
.
interceptors
.
request
.
use
(
config
=>
{
const
user
=
getState
().
usersState
.
user
;
if
(
user
)
{
config
.
headers
.
Authorization
=
user
.
token
;
}
return
config
;
},
error
=>
Promise
.
reject
(
error
)
);
};
export
default
setup
;
front_client/src/store/actions/categoriesActions.js
0 → 100644
View file @
2584765b
import
axios
from
"../../api/axiosApi"
;
import
{
createAsyncThunk
}
from
"@reduxjs/toolkit"
;
export
const
fetchCategories
=
createAsyncThunk
(
'categories/fetch'
,
async
()
=>
await
axios
.
get
(
'/categories'
).
then
(
res
=>
res
.
data
)
);
front_client/src/store/actions/usersActions.js
View file @
2584765b
...
@@ -31,13 +31,14 @@ export const loginUser = createAsyncThunk(
...
@@ -31,13 +31,14 @@ export const loginUser = createAsyncThunk(
export
const
logoutUser
=
createAsyncThunk
(
export
const
logoutUser
=
createAsyncThunk
(
'users/logout'
,
'users/logout'
,
async
(
payload
,
{
dispatch
})
=>
await
axiosApi
async
(
{
callback
}
,
{
dispatch
})
=>
await
axiosApi
.
delete
(
'/users/logout'
)
.
delete
(
'/users/logout'
)
.
then
(
res
=>
{
.
then
(
res
=>
{
dispatch
(
setUser
(
null
));
dispatch
(
setUser
(
null
));
payload
.
callback
();
callback
();
})
})
.
catch
(
e
=>
{
.
catch
(
e
=>
{
dispatch
(
setUser
(
null
));
if
(
e
?.
response
?.
data
)
dispatch
(
setLogoutError
(
e
.
response
.
data
));
if
(
e
?.
response
?.
data
)
dispatch
(
setLogoutError
(
e
.
response
.
data
));
else
dispatch
(
setLogoutError
(
e
));
else
dispatch
(
setLogoutError
(
e
));
throw
e
;
throw
e
;
...
...
front_client/src/store/configureStore.js
0 → 100644
View file @
2584765b
import
{
configureStore
}
from
"@reduxjs/toolkit"
;
import
usersReducer
from
"./services/usersSlice"
;
import
productsReducer
from
"./services/productsSlice"
;
import
categoriesReducer
from
"./services/categoriesSlice"
;
const
localStorageMiddleware
=
({
getState
})
=>
next
=>
action
=>
{
const
result
=
next
(
action
);
if
(
getState
().
usersState
.
user
)
{
localStorage
.
setItem
(
'user'
,
JSON
.
stringify
(
getState
().
usersState
.
user
));
}
else
{
localStorage
.
removeItem
(
'user'
);
}
return
result
;
};
const
reHydrateStore
=
()
=>
{
const
userLocalStorage
=
localStorage
.
getItem
(
'user'
);
if
(
userLocalStorage
!==
null
||
userLocalStorage
!==
'null'
||
userLocalStorage
!==
''
)
{
return
{
usersState
:
{
user
:
JSON
.
parse
(
localStorage
.
getItem
(
'user'
))
}
};
}
return
undefined
;
};
const
store
=
configureStore
({
reducer
:
{
usersState
:
usersReducer
,
productsState
:
productsReducer
,
categoriesState
:
categoriesReducer
,
},
preloadedState
:
reHydrateStore
(),
middleware
:
getDefaultMiddleware
=>
getDefaultMiddleware
().
concat
(
localStorageMiddleware
)
});
export
default
store
;
front_client/src/store/services/categoriesSlice.js
0 → 100644
View file @
2584765b
import
{
createSlice
}
from
"@reduxjs/toolkit"
;
import
{
fetchCategories
}
from
"../actions/categoriesActions"
;
const
initialState
=
{
categories
:
[]
};
const
categoriesSlice
=
createSlice
(({
name
:
'categories'
,
initialState
,
extraReducers
:
builder
=>
{
builder
.
addCase
(
fetchCategories
.
fulfilled
,
(
state
,
{
payload
})
=>
{
state
.
categories
=
payload
||
[];
}
);
}
}));
export
default
categoriesSlice
.
reducer
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment