Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
H
Homework83_M11
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
Ли Джен Сеп
Homework83_M11
Commits
6fdc5364
Commit
6fdc5364
authored
Dec 20, 2024
by
Ли Джен Сеп
💬
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'updateFrontend' into 'master'
#6
рефакторю код. соеденил фронт и бек See merge request
!15
parents
6ab37d66
2078e52e
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
296 additions
and
299 deletions
+296
-299
.prettierrc
frontend/.prettierrc
+6
-6
Header.tsx
frontend/src/app/components/Header/Header.tsx
+17
-0
InputField.tsx
frontend/src/app/components/InputField.tsx
+51
-50
layout.tsx
frontend/src/app/layout.tsx
+12
-12
page.tsx
frontend/src/app/page.tsx
+138
-156
requestSlice.ts
frontend/src/features/requestSlice.ts
+58
-65
axiosClient.ts
frontend/src/helpers/axiosClient.ts
+2
-4
hooks.ts
frontend/src/store/hooks.ts
+3
-3
store.ts
frontend/src/store/store.ts
+3
-3
main.ts
service/src/main.ts
+6
-0
No files found.
frontend/.prettierrc
View file @
6fdc5364
{
{
"semi": true,
"semi": true,
"singleQuote": true,
"singleQuote": true,
"tabWidth": 2,
"tabWidth": 3,
"trailingComma": "es5",
"trailingComma": "es5",
"printWidth": 80
"printWidth": 140
}
}
\ No newline at end of file
frontend/src/app/components/Header/Header.tsx
0 → 100644
View file @
6fdc5364
import
{
Typography
}
from
'@mui/material'
;
export
function
Header
()
{
return
(
<
Typography
variant=
"h3"
sx=
{
{
fontSize
:
{
xs
:
'24px'
,
sm
:
'36px'
,
},
}
}
>
Cypher Coder/Decoder
</
Typography
>
);
}
frontend/src/app/components/InputField.tsx
View file @
6fdc5364
import
{
Grid2
,
TextField
,
Typography
}
from
"@mui/material"
;
import
{
Grid2
,
TextField
,
Typography
}
from
'@mui/material'
;
import
{
ChangeEvent
}
from
"react"
;
import
{
ChangeEvent
}
from
'react'
;
interface
Props
{
interface
Props
{
name
:
"Decoded"
|
"Encoded"
|
"Password"
;
name
:
'Decoded'
|
'Encoded'
|
'Password'
;
value
:
string
;
value
:
string
;
onChange
:
(
e
:
ChangeEvent
<
HTMLInputElement
>
)
=>
void
;
onChange
:
(
e
:
ChangeEvent
<
HTMLInputElement
>
)
=>
void
;
}
}
export
default
function
InputField
({
name
,
value
,
onChange
}:
Props
)
{
export
default
function
InputField
({
name
,
value
,
onChange
}:
Props
)
{
let
labelText
:
string
=
""
;
let
labelText
:
string
=
''
;
switch
(
name
)
{
switch
(
name
)
{
case
"Decoded"
:
case
'Decoded'
:
labelText
=
`Decoded message`
;
labelText
=
`Decoded message`
;
break
;
break
;
case
"Encoded"
:
case
'Encoded'
:
labelText
=
`Encoded message`
;
labelText
=
`Encoded message`
;
break
;
break
;
case
"Password"
:
case
'Password'
:
labelText
=
""
;
labelText
=
''
;
break
;
break
;
default
:
default
:
break
;
break
;
}
}
return
(
return
(
<
Grid2
container
alignItems=
"center"
paddingY=
{
2
}
spacing=
{
2
}
>
<
Grid2
container
alignItems=
"center"
paddingY=
{
2
}
spacing=
{
2
}
>
<
Grid2
flexGrow=
{
1
}
>
<
Grid2
flexGrow=
{
1
}
>
<
Typography
<
Typography
component=
"label"
component=
"label"
htmlFor=
{
`${name}-input`
}
htmlFor=
{
`${name}-input`
}
sx=
{
{
sx=
{
{
fontSize
:
{
fontSize
:
{
xs
:
"16px"
,
xs
:
'16px'
,
sm
:
"20px"
,
sm
:
'20px'
,
},
},
}
}
}
}
>
>
{
labelText
}
{
labelText
}
</
Typography
>
</
Typography
>
</
Grid2
>
</
Grid2
>
<
Grid2
size=
{
name
===
"Password"
?
6
:
8
}
sx=
{
{
"@media (max-width: 900px)"
:
{
width
:
"100%"
}
}
}
>
<
Grid2
size=
{
name
===
'Password'
?
6
:
8
}
sx=
{
{
'@media (max-width: 900px)'
:
{
width
:
'100%'
}
}
}
>
<
TextField
<
TextField
fullWidth
fullWidth
multiline=
{
name
!==
"Password"
}
multiline=
{
name
!==
'Password'
}
rows=
{
name
===
"Password"
?
1
:
6
}
rows=
{
name
===
'Password'
?
1
:
6
}
variant=
"outlined"
variant=
"outlined"
id=
{
`${name}-input`
}
id=
{
`${name}-input`
}
name=
{
name
}
name=
{
name
}
label=
{
name
}
label=
{
name
}
value=
{
value
}
value=
{
value
}
onChange=
{
onChange
}
type=
"password"
/>
onChange=
{
onChange
}
</
Grid2
>
/>
</
Grid2
>
</
Grid2
>
);
</
Grid2
>
);
}
}
frontend/src/app/layout.tsx
View file @
6fdc5364
"use client"
;
'use client'
;
import
{
store
}
from
"@/store/store"
;
import
{
store
}
from
'@/store/store'
;
import
{
Provider
}
from
"react-redux"
;
import
{
Provider
}
from
'react-redux'
;
export
default
function
RootLayout
({
export
default
function
RootLayout
({
children
,
children
,
}:
Readonly
<
{
}:
Readonly
<
{
children
:
React
.
ReactNode
;
children
:
React
.
ReactNode
;
}
>
)
{
}
>
)
{
return
(
return
(
<
Provider
store=
{
store
}
>
<
Provider
store=
{
store
}
>
<
html
lang=
"en"
>
<
html
lang=
"en"
>
<
body
>
{
children
}
</
body
>
<
body
>
{
children
}
</
body
>
</
html
>
</
html
>
</
Provider
>
</
Provider
>
);
);
}
}
frontend/src/app/page.tsx
View file @
6fdc5364
'use client'
;
'use client'
;
import
{
ChangeEvent
,
useEffect
,
useState
}
from
'react'
;
import
{
Box
,
Button
,
Container
,
Grid2
,
Typography
}
from
'@mui/material'
;
import
{
Box
,
Button
,
Container
,
Grid2
,
Typography
}
from
'@mui/material'
;
import
ArrowDownwardIcon
from
'@mui/icons-material/ArrowDownward'
;
import
ArrowDownwardIcon
from
'@mui/icons-material/ArrowDownward'
;
import
{
ChangeEvent
,
useEffect
,
useState
}
from
'react'
;
import
InputField
from
'./components/InputField'
;
import
InputField
from
'./components/InputField'
;
import
'@/animations/Loader.css'
;
import
'@/animations/Loader.css'
;
import
{
useAppDispatch
,
useAppSelector
}
from
'@/store/hooks'
;
import
{
useAppDispatch
,
useAppSelector
}
from
'@/store/hooks'
;
import
{
Header
}
from
'./components/Header/Header'
;
import
{
encodeMessage
,
decodeMessage
}
from
'@/features/requestSlice'
;
import
{
encodeMessage
,
decodeMessage
}
from
'@/features/requestSlice'
;
interface
IFormData
{
interface
IFormData
{
decoded
:
string
;
decoded
:
string
;
encoded
:
string
;
encoded
:
string
;
password
:
string
;
password
:
string
;
}
}
export
default
function
Home
()
{
export
default
function
Home
()
{
const
dispatch
=
useAppDispatch
();
const
dispatch
=
useAppDispatch
();
const
{
loading
,
result
,
error
}
=
useAppSelector
((
state
)
=>
state
.
request
);
const
{
loading
,
result
,
error
}
=
useAppSelector
((
state
)
=>
state
.
request
);
const
[
formData
,
setFormData
]
=
useState
<
IFormData
>
({
encoded
:
''
,
decoded
:
''
,
password
:
''
,
});
const
[
formData
,
setFormData
]
=
useState
<
IFormData
>
({
const
onCypherHandler
=
():
void
=>
{
encoded
:
''
,
if
(
!
formData
.
password
.
trim
())
{
decoded
:
''
,
alert
(
'Please, type in password!'
);
password
:
''
,
return
;
});
}
dispatch
(
encodeMessage
({
password
:
formData
.
password
,
message
:
formData
.
decoded
,
})
);
};
const
onCypherHandler
=
():
void
=>
{
const
onDecypherHandler
=
():
void
=>
{
if
(
!
formData
.
password
.
trim
())
{
if
(
!
formData
.
password
.
trim
())
{
alert
(
'Please, type in password!'
);
alert
(
'Please, type in password!'
);
return
;
return
;
}
}
if
(
!
formData
.
encoded
.
trim
())
{
alert
(
'Please fill in the "Decoded" field!'
);
return
;
}
dispatch
(
encodeMessage
({
password
:
formData
.
password
,
message
:
formData
.
decoded
})
);
};
const
onDecypherHandler
=
():
void
=>
{
dispatch
(
if
(
!
formData
.
password
.
trim
())
{
decodeMessage
({
alert
(
'Please, type in password!'
);
password
:
formData
.
password
,
return
;
message
:
formData
.
encoded
,
}
})
if
(
!
formData
.
decoded
.
trim
())
{
);
alert
(
'Please fill in the "Decoded" field!'
);
};
return
;
}
dispatch
(
decodeMessage
({
password
:
formData
.
password
,
message
:
formData
.
decoded
})
);
};
useEffect
(()
=>
{
useEffect
(()
=>
{
if
(
result
)
{
if
(
result
)
{
if
(
formData
.
decoded
)
{
if
(
formData
.
decoded
)
{
setFormData
((
prev
)
=>
({
...
prev
,
encoded
:
result
}));
setFormData
((
prev
)
=>
({
...
prev
,
encoded
:
result
}));
}
else
{
}
else
{
setFormData
((
prev
)
=>
({
...
prev
,
decoded
:
result
}));
setFormData
((
prev
)
=>
({
...
prev
,
decoded
:
result
}));
}
}
}
}
},
[
result
]);
},
[
result
]);
const
onInputChangeHandler
=
(
e
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
onInputChangeHandler
=
(
e
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
const
{
name
,
value
}
=
e
.
target
;
const
{
name
,
value
}
=
e
.
target
;
setFormData
((
prevData
)
=>
({
setFormData
((
prevData
)
=>
({
...
prevData
,
...
prevData
,
[
name
.
toLowerCase
()]:
value
,
[
name
.
toLowerCase
()]:
value
,
}));
}));
};
};
return
(
return
(
<
div
className=
"App"
>
<
div
className=
"App"
>
<
Container
sx=
{
{
marginTop
:
2
}
}
maxWidth=
"lg"
>
<
Container
sx=
{
{
marginTop
:
2
}
}
maxWidth=
"lg"
>
<
Typography
<
Header
/>
variant=
"h3"
<
Box
sx=
{
{
sx=
{
{
fontSize
:
{
maxWidth
:
{
xs
:
'24px'
,
xs
:
'90%'
,
sm
:
'36px'
,
sm
:
'80%'
,
},
lg
:
'70%'
,
}
}
>
Cypher Coder/Decoder
</
Typography
>
<
Box
sx=
{
{
maxWidth
:
{
xs
:
'90%'
,
sm
:
'80%'
,
lg
:
'70%'
,
},
}
}
component=
"form"
autoComplete=
"off"
padding=
{
2
}
>
<
Grid2
container
direction=
"column"
spacing=
{
2
}
>
<
InputField
name=
"Decoded"
value=
{
formData
.
decoded
}
onChange=
{
onInputChangeHandler
}
/>
<
Grid2
container
alignItems=
"center"
sx=
{
{
flexDirection
:
{
xs
:
'column'
,
md
:
'row'
,
},
},
}
}
}
}
>
component=
"form"
<
Grid2
size=
{
7
}
>
autoComplete=
"off"
<
InputField
padding=
{
2
}
name=
"Password"
>
value=
{
formData
.
password
}
<
Grid2
container
direction=
"column"
spacing=
{
2
}
>
onChange=
{
onInputChangeHandler
}
<
InputField
name=
"Decoded"
value=
{
formData
.
decoded
}
onChange=
{
onInputChangeHandler
}
/>
/>
<
Grid2
</
Grid2
>
container
<
Grid2
container
>
alignItems=
"center"
<
Grid2
>
sx=
{
{
<
Button
flexDirection
:
{
size=
"small"
xs
:
'column'
,
variant=
"contained"
md
:
'row'
,
startIcon=
{
<
ArrowDownwardIcon
/>
}
},
onClick=
{
onDecypherHandler
}
}
}
>
>
<
Typography
<
Grid2
size=
{
7
}
>
sx=
{
{
<
InputField
name=
"Password"
value=
{
formData
.
password
}
onChange=
{
onInputChangeHandler
}
/>
display
:
{
xs
:
'none'
,
md
:
'inline'
},
</
Grid2
>
}
}
<
Grid2
container
>
>
<
Grid2
>
Encode
<
Button
size=
"small"
variant=
"contained"
startIcon=
{
<
ArrowDownwardIcon
/>
}
onClick=
{
onDecypherHandler
}
>
</
Typography
>
<
Typography
</
Button
>
sx=
{
{
</
Grid2
>
display
:
{
xs
:
'none'
,
md
:
'inline'
,
},
}
}
>
Decode
</
Typography
>
</
Button
>
</
Grid2
>
<
Grid2
>
<
Grid2
>
<
Button
<
Button
size=
"small"
size=
"small"
variant=
"contained"
variant=
"contained"
startIcon=
{
startIcon=
{
<
ArrowDownwardIcon
<
ArrowDownwardIcon
sx=
{
{
transform
:
'rotate(180deg)'
}
}
sx=
{
{
/>
transform
:
'rotate(180deg)'
,
}
}
}
onClick=
{
onCypherHandler
}
/>
>
}
<
Typography
onClick=
{
()
=>
onCypherHandler
()
}
sx=
{
{
>
display
:
{
xs
:
'none'
,
md
:
'inline'
},
<
Typography
}
}
sx=
{
{
>
display
:
{
Decode
xs
:
'none'
,
</
Typography
>
md
:
'inline'
,
</
Button
>
},
}
}
>
Encode
</
Typography
>
</
Button
>
</
Grid2
>
</
Grid2
>
</
Grid2
>
</
Grid2
>
</
Grid2
>
</
Grid2
>
<
InputField
<
InputField
name=
"Encoded"
value=
{
formData
.
encoded
}
onChange=
{
onInputChangeHandler
}
/>
name=
"Encoded"
</
Grid2
>
value=
{
formData
.
encoded
}
</
Box
>
onChange=
{
onInputChangeHandler
}
/>
</
Grid2
>
</
Box
>
{
loading
&&
(
{
loading
&&
(
<
div
className=
"loader"
>
<
div
className=
"loader"
>
<
div
className=
"lds-ripple"
>
<
div
className=
"lds-ripple"
>
<
div
></
div
>
<
div
></
div
>
<
div
></
div
>
<
div
></
div
>
</
div
>
</
div
>
</
div
>
</
div
>
)
}
)
}
{
error
&&
<
Typography
color=
"error"
>
{
error
}
</
Typography
>
}
{
error
&&
<
Typography
color=
"error"
>
{
error
}
</
Typography
>
}
</
Container
>
</
Container
>
</
div
>
</
div
>
);
);
}
}
frontend/src/features/requestSlice.ts
View file @
6fdc5364
import
{
createSlice
,
createAsyncThunk
}
from
'@reduxjs/toolkit'
;
import
{
createSlice
,
createAsyncThunk
}
from
'@reduxjs/toolkit'
;
import
axiosClient
from
'@/helpers/axiosClient'
;
import
{
axiosClient
}
from
'@/helpers/axiosClient'
;
interface
RequestState
{
interface
RequestState
{
loading
:
boolean
;
loading
:
boolean
;
error
?:
string
|
null
;
error
?:
string
|
null
;
result
:
string
;
result
:
string
;
}
}
interface
IData
{
interface
IData
{
password
:
string
;
password
:
string
;
message
:
string
;
message
:
string
;
}
}
const
initialState
:
RequestState
=
{
const
initialState
:
RequestState
=
{
loading
:
false
,
loading
:
false
,
error
:
null
,
error
:
null
,
result
:
''
,
result
:
''
,
};
};
export
const
encodeMessage
=
createAsyncThunk
(
export
const
encodeMessage
=
createAsyncThunk
(
'request/encode'
,
async
(
newMessage
:
IData
)
=>
{
'request/encode'
,
try
{
async
(
data
:
IData
)
=>
{
const
{
data
}
=
await
axiosClient
.
post
(
'/encode'
,
newMessage
);
try
{
console
.
log
(
data
);
const
response
=
await
axiosClient
.
post
(
'/encode'
,
data
);
return
response
.
data
.
encoded
;
}
catch
(
error
:
any
)
{
throw
new
Error
(
error
.
response
?.
data
?.
message
||
'Error decoding message'
);
}
}
);
export
const
decodeMessage
=
createAsyncThunk
(
return
data
.
encoded
;
'request/decode'
,
}
catch
(
error
:
any
)
{
async
(
data
:
IData
)
=>
{
throw
new
Error
(
error
.
response
?.
data
?.
message
||
'Error decoding message'
);
try
{
}
const
response
=
await
axiosClient
.
post
(
'/decode'
,
data
);
});
return
response
.
data
.
decoded
;
}
catch
(
error
:
any
)
{
export
const
decodeMessage
=
createAsyncThunk
(
'request/decode'
,
async
(
newMessage
:
IData
)
=>
{
throw
new
Error
(
try
{
error
.
response
?.
data
?.
message
||
'Error encoding message'
const
{
data
}
=
await
axiosClient
.
post
(
'/decode'
,
newMessage
);
);
console
.
log
(
data
);
}
return
data
.
decoded
;
}
}
catch
(
error
:
any
)
{
);
throw
new
Error
(
error
.
response
?.
data
?.
message
||
'Error encoding message'
);
}
});
const
requestSlice
=
createSlice
({
const
requestSlice
=
createSlice
({
name
:
'request'
,
name
:
'request'
,
initialState
,
initialState
,
reducers
:
{},
reducers
:
{},
extraReducers
:
(
builder
)
=>
{
extraReducers
:
(
builder
)
=>
{
builder
builder
.
addCase
(
encodeMessage
.
pending
,
(
state
)
=>
{
.
addCase
(
encodeMessage
.
pending
,
(
state
)
=>
{
state
.
loading
=
true
;
state
.
loading
=
true
;
state
.
error
=
null
;
state
.
error
=
null
;
})
})
.
addCase
(
encodeMessage
.
fulfilled
,
(
state
,
action
)
=>
{
.
addCase
(
encodeMessage
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loading
=
false
;
state
.
loading
=
false
;
state
.
result
=
action
.
payload
;
state
.
result
=
action
.
payload
;
})
})
.
addCase
(
encodeMessage
.
rejected
,
(
state
,
action
)
=>
{
.
addCase
(
encodeMessage
.
rejected
,
(
state
,
action
)
=>
{
state
.
loading
=
false
;
state
.
loading
=
false
;
state
.
error
=
action
.
error
.
message
||
"Error occured"
;
state
.
error
=
action
.
error
.
message
||
'Error occured'
;
})
})
.
addCase
(
decodeMessage
.
pending
,
(
state
)
=>
{
.
addCase
(
decodeMessage
.
pending
,
(
state
)
=>
{
state
.
loading
=
true
;
state
.
loading
=
true
;
state
.
error
=
null
;
state
.
error
=
null
;
})
})
.
addCase
(
decodeMessage
.
fulfilled
,
(
state
,
action
)
=>
{
.
addCase
(
decodeMessage
.
fulfilled
,
(
state
,
action
)
=>
{
state
.
loading
=
false
;
state
.
loading
=
false
;
state
.
result
=
action
.
payload
;
state
.
result
=
action
.
payload
;
})
})
.
addCase
(
decodeMessage
.
rejected
,
(
state
,
action
)
=>
{
.
addCase
(
decodeMessage
.
rejected
,
(
state
,
action
)
=>
{
state
.
loading
=
false
;
state
.
loading
=
false
;
state
.
error
=
action
.
error
.
message
||
"Error occured"
;
state
.
error
=
action
.
error
.
message
||
'Error occured'
;
});
});
},
},
});
});
export
default
requestSlice
.
reducer
;
export
default
requestSlice
.
reducer
;
frontend/src/helpers/axiosClient.ts
View file @
6fdc5364
import
axios
from
'axios'
;
import
axios
from
'axios'
;
const
axiosClient
=
axios
.
create
({
export
const
axiosClient
=
axios
.
create
({
baseURL
:
'https://localhost/
8000'
,
baseURL
:
'http://localhost:
8000'
,
});
});
export
default
axiosClient
;
frontend/src/store/hooks.ts
View file @
6fdc5364
import
{
useDispatch
,
useSelector
}
from
"react-redux"
;
import
{
useDispatch
,
useSelector
}
from
'react-redux'
;
import
type
{
TypedUseSelectorHook
}
from
"react-redux"
;
import
type
{
TypedUseSelectorHook
}
from
'react-redux'
;
import
type
{
RootState
,
AppDispatch
}
from
"./store"
;
import
type
{
RootState
,
AppDispatch
}
from
'./store'
;
export
const
useAppDispatch
:
()
=>
AppDispatch
=
useDispatch
;
export
const
useAppDispatch
:
()
=>
AppDispatch
=
useDispatch
;
export
const
useAppSelector
:
TypedUseSelectorHook
<
RootState
>
=
useSelector
;
export
const
useAppSelector
:
TypedUseSelectorHook
<
RootState
>
=
useSelector
;
frontend/src/store/store.ts
View file @
6fdc5364
...
@@ -2,9 +2,9 @@ import { configureStore } from '@reduxjs/toolkit';
...
@@ -2,9 +2,9 @@ import { configureStore } from '@reduxjs/toolkit';
import
requestReducer
from
'@/features/requestSlice'
;
import
requestReducer
from
'@/features/requestSlice'
;
export
const
store
=
configureStore
({
export
const
store
=
configureStore
({
reducer
:
{
reducer
:
{
request
:
requestReducer
,
request
:
requestReducer
,
},
},
});
});
export
type
RootState
=
ReturnType
<
typeof
store
.
getState
>
;
export
type
RootState
=
ReturnType
<
typeof
store
.
getState
>
;
...
...
service/src/main.ts
View file @
6fdc5364
...
@@ -6,6 +6,12 @@ ConfigModule.forRoot();
...
@@ -6,6 +6,12 @@ ConfigModule.forRoot();
async
function
startServer
()
{
async
function
startServer
()
{
const
app
=
await
NestFactory
.
create
(
MessageModule
);
const
app
=
await
NestFactory
.
create
(
MessageModule
);
app
.
enableCors
({
origin
:
'http://localhost:666'
,
methods
:
'GET,POST,PUT,DELETE'
,
allowedHeaders
:
'Content-Type,Authorization'
,
credentials
:
true
,
});
await
app
.
listen
(
8000
);
await
app
.
listen
(
8000
);
}
}
startServer
();
startServer
();
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