Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
E
exam_12_Tsoy_Danil
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
Цой Данил
exam_12_Tsoy_Danil
Commits
08c07225
Commit
08c07225
authored
Apr 15, 2023
by
Цой Данил
💬
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added stores for photos and users + added privateroute
parent
0917317f
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
333 additions
and
83 deletions
+333
-83
index.html
frontend/index.html
+1
-0
App.css
frontend/src/App.css
+0
-42
index.css
frontend/src/index.css
+10
-37
main.tsx
frontend/src/main.tsx
+8
-4
createAppAsyncThunk.ts
frontend/src/store/createAppAsyncThunk.ts
+8
-0
IPhotosState.ts
frontend/src/store/photos/IPhotosState.ts
+9
-0
photos.slice.ts
frontend/src/store/photos/photos.slice.ts
+106
-0
store.ts
frontend/src/store/store.ts
+44
-0
IUserState.ts
frontend/src/store/user/IUserState.ts
+9
-0
user.slice.ts
frontend/src/store/user/user.slice.ts
+110
-0
PrivateRoute.tsx
frontend/src/utils/PrivateRoute.tsx
+28
-0
No files found.
frontend/index.html
View file @
08c07225
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
<head>
<head>
<meta
charset=
"UTF-8"
/>
<meta
charset=
"UTF-8"
/>
<link
rel=
"icon"
type=
"image/svg+xml"
href=
"/vite.svg"
/>
<link
rel=
"icon"
type=
"image/svg+xml"
href=
"/vite.svg"
/>
<link
href=
"https://fonts.googleapis.com/css?family=Kanit:100,100italic,200,200italic,300,300italic,regular,italic,500,500italic,600,600italic,700,700italic,800,800italic,900,900italic"
rel=
"stylesheet"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
/>
<title>
Vite + React + TS
</title>
<title>
Vite + React + TS
</title>
</head>
</head>
...
...
frontend/src/App.css
View file @
08c07225
#root
{
max-width
:
1280px
;
margin
:
0
auto
;
padding
:
2rem
;
text-align
:
center
;
}
.logo
{
height
:
6em
;
padding
:
1.5em
;
will-change
:
filter
;
transition
:
filter
300ms
;
}
.logo
:hover
{
filter
:
drop-shadow
(
0
0
2em
#646cff
aa
);
}
.logo.react
:hover
{
filter
:
drop-shadow
(
0
0
2em
#61dafb
aa
);
}
@keyframes
logo-spin
{
from
{
transform
:
rotate
(
0deg
);
}
to
{
transform
:
rotate
(
360deg
);
}
}
@media
(
prefers-reduced-motion
:
no-preference
)
{
a
:
nth-of-type
(
2
)
.
logo
{
animation
:
logo-spin
infinite
20s
linear
;
}
}
.card
{
padding
:
2em
;
}
.read-the-docs
{
color
:
#888
;
}
frontend/src/index.css
View file @
08c07225
...
@@ -25,45 +25,18 @@ a:hover {
...
@@ -25,45 +25,18 @@ a:hover {
body
{
body
{
margin
:
0
;
margin
:
0
;
display
:
flex
;
font-family
:
'Kanit'
,
sans-serif
;
place-items
:
center
;
background-position
:
center
top
;
min-width
:
320px
;
min-height
:
100vh
;
min-height
:
100vh
;
height
:
100%
;
background
:
url('../src/assets/background.jpg')
;
}
}
h1
{
button
{
font-size
:
3.2em
;
font-family
:
'Kanit'
,
sans-serif
;
line-height
:
1.1
;
}
}
button
{
.active
{
border-radius
:
8px
;
color
:
black
!important
;
border
:
1px
solid
transparent
;
border-bottom
:
2px
solid
black
;
padding
:
0.6em
1.2em
;
font-size
:
1em
;
font-weight
:
500
;
font-family
:
inherit
;
background-color
:
#1a1a1a
;
cursor
:
pointer
;
transition
:
border-color
0.25s
;
}
button
:hover
{
border-color
:
#646cff
;
}
button
:focus
,
button
:focus-visible
{
outline
:
4px
auto
-webkit-focus-ring-color
;
}
@media
(
prefers-color-scheme
:
light
)
{
:root
{
color
:
#213547
;
background-color
:
#ffffff
;
}
a
:hover
{
color
:
#747bff
;
}
button
{
background-color
:
#f9f9f9
;
}
}
}
\ No newline at end of file
frontend/src/main.tsx
View file @
08c07225
import
React
from
'react'
import
ReactDOM
from
'react-dom/client'
import
ReactDOM
from
'react-dom/client'
import
App
from
'./App'
import
App
from
'./App'
import
'./index.css'
import
'./index.css'
import
{
Provider
}
from
'react-redux'
import
{
BrowserRouter
}
from
'react-router-dom'
import
store
from
'./store/store'
ReactDOM
.
createRoot
(
document
.
getElementById
(
'root'
)
as
HTMLElement
).
render
(
ReactDOM
.
createRoot
(
document
.
getElementById
(
'root'
)
as
HTMLElement
).
render
(
<
React
.
StrictMode
>
<
Provider
store=
{
store
}
>
<
BrowserRouter
>
<
App
/>
<
App
/>
</
React
.
StrictMode
>,
</
BrowserRouter
>
</
Provider
>
)
)
frontend/src/store/createAppAsyncThunk.ts
0 → 100644
View file @
08c07225
import
{
createAsyncThunk
}
from
"@reduxjs/toolkit"
import
{
AppDispatch
,
AppState
}
from
"./store"
export
const
createAppAsyncThunk
=
createAsyncThunk
.
withTypes
<
{
state
:
AppState
dispatch
:
AppDispatch
rejectValue
:
string
}
>
()
\ No newline at end of file
frontend/src/store/photos/IPhotosState.ts
0 → 100644
View file @
08c07225
import
IPhoto
from
"../../interfaces/IPhoto"
;
import
IUser
from
"../../interfaces/IUser"
;
export
default
interface
IPhotosState
{
photos
:
IPhoto
[]
photosByUser
:
IPhoto
[]
targetedUser
:
IUser
loadingPhotos
:
boolean
}
\ No newline at end of file
frontend/src/store/photos/photos.slice.ts
0 → 100644
View file @
08c07225
import
{
createSlice
}
from
"@reduxjs/toolkit"
import
{
photosApi
}
from
"../../api/photosApi"
import
{
createAppAsyncThunk
}
from
"../createAppAsyncThunk"
import
IPhotosState
from
"./IPhotosState"
import
IPhoto
from
"../../interfaces/IPhoto"
import
IUser
from
"../../interfaces/IUser"
const
namespace
:
string
=
'photos'
export
const
getAllPhotos
=
createAppAsyncThunk
(
`
${
namespace
}
/getAllPhotos`
,
async
()
=>
{
return
await
photosApi
.
getAllPhotos
()
}
)
export
const
addPhoto
=
createAppAsyncThunk
(
`
${
namespace
}
/addPhoto`
,
async
(
photoDto
:
FormData
)
=>
{
return
await
photosApi
.
addPhoto
(
photoDto
)
}
)
export
const
getPhotosByUserId
=
createAppAsyncThunk
(
`
${
namespace
}
/getPhotosByUserId`
,
async
(
id
:
string
)
=>
{
return
await
photosApi
.
getPhotosByUserId
(
id
)
}
)
export
const
deletePhotoById
=
createAppAsyncThunk
(
`
${
namespace
}
/deletePhotoById`
,
async
(
id
:
string
)
=>
{
return
await
photosApi
.
deletePhotoById
(
id
)
}
)
const
initialState
:
IPhotosState
=
{
photos
:
[],
photosByUser
:
[],
targetedUser
:
{}
as
IUser
,
loadingPhotos
:
false
}
export
const
photosSlice
=
createSlice
({
name
:
namespace
,
initialState
:
initialState
,
reducers
:
{
setTargetedUser
(
state
,
action
){
state
.
targetedUser
=
action
.
payload
}
},
extraReducers
:
(
builder
)
=>
{
builder
.
addCase
(
getAllPhotos
.
pending
,
(
state
)
=>
{
state
.
loadingPhotos
=
true
})
.
addCase
(
getAllPhotos
.
rejected
,
(
state
)
=>
{
state
.
loadingPhotos
=
false
})
.
addCase
(
getAllPhotos
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loadingPhotos
=
false
if
(
action
.
payload
.
result
)
state
.
photos
=
action
.
payload
.
result
})
.
addCase
(
getPhotosByUserId
.
pending
,
(
state
)
=>
{
state
.
loadingPhotos
=
true
})
.
addCase
(
getPhotosByUserId
.
rejected
,
(
state
)
=>
{
state
.
loadingPhotos
=
false
})
.
addCase
(
getPhotosByUserId
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loadingPhotos
=
false
if
(
action
.
payload
.
result
)
state
.
photosByUser
=
action
.
payload
.
result
})
.
addCase
(
addPhoto
.
pending
,
(
state
)
=>
{
state
.
loadingPhotos
=
true
})
.
addCase
(
addPhoto
.
rejected
,
(
state
)
=>
{
state
.
loadingPhotos
=
false
})
.
addCase
(
addPhoto
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loadingPhotos
=
false
if
(
action
.
payload
.
result
)
state
.
photos
.
push
(
action
.
payload
.
result
)
})
.
addCase
(
deletePhotoById
.
pending
,
(
state
)
=>
{
state
.
loadingPhotos
=
true
})
.
addCase
(
deletePhotoById
.
rejected
,
(
state
)
=>
{
state
.
loadingPhotos
=
false
})
.
addCase
(
deletePhotoById
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loadingPhotos
=
false
if
(
action
.
payload
.
result
){
state
.
photosByUser
=
state
.
photosByUser
.
filter
((
photo
:
IPhoto
)
=>
{
return
photo
.
_id
!==
action
.
payload
.
result
?.
_id
})
}
})
}
})
export
const
{
setTargetedUser
}
=
photosSlice
.
actions
\ No newline at end of file
frontend/src/store/store.ts
0 → 100644
View file @
08c07225
import
{
configureStore
,
ThunkAction
,
Action
}
from
"@reduxjs/toolkit"
import
{
useDispatch
}
from
'react-redux'
import
{
usersSlice
}
from
"./user/user.slice"
;
import
{
photosSlice
}
from
"./photos/photos.slice"
;
const
localStorageMiddleware
=
({
getState
}:
any
)
=>
{
return
(
next
:
any
)
=>
(
action
:
any
)
=>
{
const
result
=
next
(
action
);
localStorage
.
setItem
(
'applicationState'
,
JSON
.
stringify
(
getState
()));
return
result
;
};
};
const
reHydrateStore
=
()
=>
{
if
(
localStorage
.
getItem
(
'applicationState'
)
!==
null
)
{
return
JSON
.
parse
(
localStorage
.
getItem
(
'applicationState'
)
||
''
);
}
};
const
makeStore
=
()
=>
{
return
configureStore
({
reducer
:{
users
:
usersSlice
.
reducer
,
photos
:
photosSlice
.
reducer
},
preloadedState
:
reHydrateStore
(),
middleware
:
(
mw
)
=>
mw
().
concat
(
localStorageMiddleware
)
})
}
const
store
=
makeStore
()
export
type
AppDispatch
=
typeof
store
.
dispatch
;
export
type
AppStore
=
ReturnType
<
typeof
makeStore
>
;
export
type
AppState
=
ReturnType
<
AppStore
[
"getState"
]
>
;
export
type
AppThunk
<
ReturnType
=
void
>
=
ThunkAction
<
ReturnType
,
AppState
,
unknown
,
Action
>
;
export
const
useAppDispatch
:
()
=>
AppDispatch
=
useDispatch
;
export
default
store
\ No newline at end of file
frontend/src/store/user/IUserState.ts
0 → 100644
View file @
08c07225
import
IUserGetDto
from
"../../interfaces/IUserGetDto"
;
export
default
interface
IUsersState
{
user
:
IUserGetDto
|
null
isAuth
:
boolean
loadingUser
:
boolean
messageUser
:
string
}
\ No newline at end of file
frontend/src/store/user/user.slice.ts
0 → 100644
View file @
08c07225
import
{
createSlice
}
from
"@reduxjs/toolkit"
import
{
EStatuses
}
from
"../../enum/EStatuses"
import
IUser
from
"../../interfaces/IUser"
import
{
createAppAsyncThunk
}
from
"../createAppAsyncThunk"
import
IUserCreateDto
from
"../../interfaces/IUserCreateDto"
import
{
usersApi
}
from
"../../api/usersApi"
import
IUsersState
from
"./IUserState"
const
namespace
:
string
=
'users'
export
const
loginUser
=
createAppAsyncThunk
(
`
${
namespace
}
/loginUser`
,
async
(
user
:
IUserCreateDto
)
=>
{
return
usersApi
.
loginUser
(
user
)
}
)
export
const
createUser
=
createAppAsyncThunk
(
`
${
namespace
}
/createUser`
,
async
(
user
:
IUserCreateDto
)
=>
{
return
await
usersApi
.
createUser
(
user
)
}
)
export
const
checkToken
=
createAppAsyncThunk
(
`
${
namespace
}
/checkToken`
,
async
()
=>
{
return
usersApi
.
checkToken
()
}
)
const
initialState
:
IUsersState
=
{
user
:
{}
as
IUser
,
isAuth
:
false
,
loadingUser
:
false
,
messageUser
:
''
}
export
const
usersSlice
=
createSlice
({
name
:
namespace
,
initialState
:
initialState
,
reducers
:
{
setToInitState
(
state
){
state
.
isAuth
=
false
state
.
user
=
{}
as
IUser
state
.
loadingUser
=
false
state
.
messageUser
=
''
},
hideMessage
(
state
){
state
.
messageUser
=
''
}
},
extraReducers
:
(
builder
)
=>
{
builder
.
addCase
(
loginUser
.
pending
,
(
state
)
=>
{
state
.
loadingUser
=
true
})
.
addCase
(
loginUser
.
rejected
,
(
state
)
=>
{
state
.
loadingUser
=
false
})
.
addCase
(
loginUser
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loadingUser
=
false
const
user
=
action
.
payload
.
result
state
.
user
=
user
if
(
action
.
payload
.
status
===
EStatuses
.
FAILURE
){
state
.
messageUser
=
action
.
payload
.
message
}
if
(
user
){
localStorage
.
setItem
(
'token'
,
user
.
token
)
state
.
isAuth
=
true
}
})
.
addCase
(
createUser
.
pending
,
(
state
)
=>
{
state
.
loadingUser
=
true
})
.
addCase
(
createUser
.
rejected
,
(
state
)
=>
{
state
.
loadingUser
=
false
})
.
addCase
(
createUser
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loadingUser
=
false
const
user
=
action
.
payload
.
result
state
.
user
=
user
if
(
action
.
payload
.
status
===
EStatuses
.
FAILURE
){
state
.
messageUser
=
action
.
payload
.
message
}
if
(
user
){
localStorage
.
setItem
(
'token'
,
user
.
token
)
state
.
isAuth
=
true
}
})
.
addCase
(
checkToken
.
pending
,
(
state
)
=>
{
state
.
loadingUser
=
true
})
.
addCase
(
checkToken
.
rejected
,
(
state
)
=>
{
state
.
loadingUser
=
false
})
.
addCase
(
checkToken
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loadingUser
=
false
const
user
=
action
.
payload
.
result
state
.
user
=
user
if
(
user
)
{
state
.
isAuth
=
true
}
else
{
state
.
isAuth
=
false
}
})
}
})
export
const
{
setToInitState
,
hideMessage
}
=
usersSlice
.
actions
\ No newline at end of file
frontend/src/utils/PrivateRoute.tsx
0 → 100644
View file @
08c07225
import
{
useEffect
}
from
"react"
import
{
shallowEqual
,
useSelector
}
from
"react-redux"
import
{
Navigate
,
Outlet
,
useLocation
}
from
"react-router-dom"
import
{
AppDispatch
,
AppState
,
useAppDispatch
}
from
"../store/store"
import
{
checkToken
}
from
"../store/user/user.slice"
const
PrivateRoute
:
React
.
FunctionComponent
=
():
React
.
ReactElement
=>
{
const
{
isAuth
}
=
useSelector
((
state
:
AppState
)
=>
state
.
users
,
shallowEqual
)
const
location
=
useLocation
()
const
dispatch
:
AppDispatch
=
useAppDispatch
()
useEffect
(()
=>
{
dispatch
(
checkToken
())
},
[])
return
(
isAuth
?
<
Outlet
/>
:
<
Navigate
to=
'/'
replace
state=
{
{
from
:
location
}
}
/>
)
}
export
default
PrivateRoute
\ No newline at end of file
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