Commit 6fdc5364 authored by Ли Джен Сеп's avatar Ли Джен Сеп 💬

Merge branch 'updateFrontend' into 'master'

#6 рефакторю код. соеденил фронт и бек

See merge request !15
parents 6ab37d66 2078e52e
{ {
"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
import { Typography } from '@mui/material';
export function Header() {
return (
<Typography
variant="h3"
sx={{
fontSize: {
xs: '24px',
sm: '36px',
},
}}
>
Cypher Coder/Decoder
</Typography>
);
}
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>
);
} }
"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>
); );
} }
'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>
); );
} }
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;
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;
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;
...@@ -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>;
......
...@@ -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();
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