Регистрация и вход работают

This commit is contained in:
Аришина) 2024-10-30 15:31:57 +04:00
parent 5be5bb06bb
commit 0abb511375
16 changed files with 419 additions and 1223 deletions

View File

@ -15,9 +15,11 @@
"@types/node": "^16.18.115", "@types/node": "^16.18.115",
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"antd": "^5.21.6", "antd": "^5.21.6",
"axios": "^1.7.7",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.2.0",
"react-scripts": "5.0.1", "react-router-dom": "^6.27.0",
"react-scripts": "^5.0.1",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
@ -3392,6 +3394,15 @@
"react-dom": ">=16.9.0" "react-dom": ">=16.9.0"
} }
}, },
"node_modules/@remix-run/router": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz",
"integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rollup/plugin-babel": { "node_modules/@rollup/plugin-babel": {
"version": "5.3.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@ -3760,16 +3771,6 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/@testing-library/dom/node_modules/aria-query": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"dequal": "^2.0.3"
}
},
"node_modules/@testing-library/jest-dom": { "node_modules/@testing-library/jest-dom": {
"version": "5.17.0", "version": "5.17.0",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz",
@ -4105,9 +4106,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "16.18.115", "version": "16.18.116",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.115.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.116.tgz",
"integrity": "sha512-NF5ajYn+dq0tRfswdyp8Df75h7D9z+L8TCIwrXoh46ZLK6KZVXkRhf/luXaZytvm/keUo9vU4m1Bg39St91a5w==", "integrity": "sha512-mLigUvhoaADRewggiby+XfAAFOUOMCm/SwL5DAJ+CMUGjSLIGMsJVN7BOKftuQSHGjUmS/W7hVht8fcNbi/MRA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node-forge": { "node_modules/@types/node-forge": {
@ -5014,12 +5015,12 @@
} }
}, },
"node_modules/aria-query": { "node_modules/aria-query": {
"version": "5.3.2", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "dependencies": {
"node": ">= 0.4" "dequal": "^2.0.3"
} }
}, },
"node_modules/array-buffer-byte-length": { "node_modules/array-buffer-byte-length": {
@ -5308,6 +5309,17 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axobject-query": { "node_modules/axobject-query": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@ -5850,9 +5862,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001673", "version": "1.0.30001675",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001673.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001675.tgz",
"integrity": "sha512-WTrjUCSMp3LYX0nE12ECkV0a+e6LC85E0Auz75555/qr78Oc8YWhEPNfDd6SHdtlCMSzqtuXY0uyEMNRcsKpKw==", "integrity": "sha512-/wV1bQwPrkLiQMjaJF5yUMVM/VdRPOCU8QZ+PmG6uW6DvYSrNY1bpwHI/3mOcUosLaJCzYDi5o91IQB51ft6cg==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -7001,7 +7013,6 @@
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@ -7864,6 +7875,15 @@
"eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
} }
}, },
"node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
"license": "Apache-2.0",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/eslint-plugin-react": { "node_modules/eslint-plugin-react": {
"version": "7.37.2", "version": "7.37.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz",
@ -8775,9 +8795,9 @@
} }
}, },
"node_modules/form-data": { "node_modules/form-data": {
"version": "3.0.2", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
@ -11301,6 +11321,20 @@
} }
} }
}, },
"node_modules/jsdom/node_modules/form-data": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz",
"integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/jsesc": { "node_modules/jsesc": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
@ -13994,6 +14028,12 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/psl": { "node_modules/psl": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@ -14910,6 +14950,38 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-router": {
"version": "6.27.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz",
"integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.20.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.27.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz",
"integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.20.0",
"react-router": "6.27.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-scripts": { "node_modules/react-scripts": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",

View File

@ -10,9 +10,11 @@
"@types/node": "^16.18.115", "@types/node": "^16.18.115",
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"antd": "^5.21.6", "antd": "^5.21.6",
"axios": "^1.7.7",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.2.0",
"react-scripts": "5.0.1", "react-router-dom": "^6.27.0",
"react-scripts": "^5.0.1",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },

View File

@ -0,0 +1,62 @@
import axios, { AxiosError, AxiosRequestHeaders } from 'axios';
import IUser from '../models/IUser';
import IFarm from '../models/IFarm';
import LoginRequest from '../Requests/LoginRequest';
import IRegisterRequest from '../Requests/RegisterRequest';
const API_BASE_URL = 'https://localhost:7113/api';
const getHeaders = (): { [key: string]: string } => {
return {
'Content-Type': 'application/json',
};
};
export const getFarms = async (): Promise<IFarm[]> => {
try {
const response = await axios.get(`${API_BASE_URL}/farms`, { headers: getHeaders() });
return response.data;
} catch (error: unknown) {
const axiosError = error as AxiosError;
console.error('Error fetching farms:', axiosError.message);
throw error;
}
};
export const getUser = async (userId: number): Promise<IUser> => {
try {
const response = await axios.get(`${API_BASE_URL}/Auth/user/${userId}`, { headers: getHeaders() });
return response.data;
} catch (error: unknown) {
const axiosError = error as AxiosError;
console.error('Error fetching user:', axiosError.message);
throw error;
}
};
export const createUser = async (userData: IRegisterRequest): Promise<IUser> => {
try {
const response = await axios.post(`${API_BASE_URL}/Auth/register`, userData, { headers: getHeaders() });
return response.data;
} catch (error: unknown) {
const axiosError = error as AxiosError;
console.error('Error creating user:', axiosError.message);
if (axiosError.response) {
// Логируем дополнительные данные об ошибке
console.error('Response data:', axiosError.response.data);
console.error('Response status:', axiosError.response.status);
}
throw error;
}
};
export const loginUser = async (userData: LoginRequest): Promise<string> => {
try {
const response = await axios.post(`${API_BASE_URL}/Auth/login`, userData, { headers: getHeaders() });
return response.data; // JWT токен
} catch (error: unknown) {
const axiosError = error as AxiosError;
console.error('Error logging in:', axiosError.message);
throw error;
}
};

View File

@ -1,11 +1,20 @@
import React from 'react'; import React from 'react';
import './App.css'; import './App.css';
import { Routes } from 'react-router-dom'; import { Navigate, Route, Routes } from 'react-router-dom';
import {LoginPage} from './pages/Login';
import {RegisterPage} from './pages/Register';
import {AppLayout} from './components/Layout';
function App() { function App() {
return ( return (
<div className="App"> <div className="App">
<Routes> <Routes>
<Route path="/" element={<AppLayout />}>
<Route index element={<Navigate to="/login" />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegisterPage />} />
</Route>
</Routes> </Routes>
</div> </div>
); );

View File

@ -0,0 +1,6 @@
interface ILoginRequest {
email: string;
password: string;
}
export default ILoginRequest;

View File

@ -0,0 +1,7 @@
interface IRegisterRequest {
name: string;
email: string;
password: string;
}
export default IRegisterRequest;

View File

@ -0,0 +1,8 @@
export function Footer () {
return (
<footer className="footer">
<p>&copy; {new Date().getFullYear()} Cucumber</p>
</footer>
);
};

View File

@ -0,0 +1,22 @@
import React from 'react';
import { Link } from 'react-router-dom';
export function Header () {
return (
<header className="header">
<div className="logo">
<Link to="/">Cucumber App</Link>
</div>
<nav className="nav">
<ul>
<li>
<Link to="/login">Login</Link>
</li>
<li>
<Link to="/register">Register</Link>
</li>
</ul>
</nav>
</header>
);
};

View File

@ -0,0 +1,19 @@
import React from 'react';
import { Layout } from 'antd';
import { Outlet } from 'react-router-dom';
import {Header} from './Header';
import {Footer} from './Footer';
export function AppLayout () {
return (
<Layout>
<Header />
<Layout.Content>
<Outlet />
</Layout.Content>
<Footer />
</Layout>
);
};
export default AppLayout;

View File

@ -11,3 +11,27 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace; monospace;
} }
header {
text-align: center;
background-color: #282c34;
min-height: 10vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
footer {
text-align: center;
background-color: #282c34;
min-height: 10vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}

View File

@ -0,0 +1,11 @@
import type IUser from "./IUser";
interface IFarm {
id: string;
name: string;
userId: number;
user: IUser | null;
rasberryMacAddr: string;
}
export default IFarm;

View File

@ -0,0 +1,11 @@
import type IFarm from "./IFarm";
interface IUser {
id: number;
name: string;
email: string;
password: string;
farms: IFarm[];
}
export default IUser

View File

@ -0,0 +1,66 @@
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { Button, Form, Input } from 'antd';
import { loginUser } from '../API/api';
import ILoginRequest from '../Requests/LoginRequest';
export function LoginPage () {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const navigate = useNavigate();
const onFinish = async () => {
// Создаём объект с данными для отправки на сервер
const userData: ILoginRequest = {
email,
password,
};
try {
const token = await loginUser(userData);
// Сохраняем токен
localStorage.setItem('token', token);
alert('Вы успешно вошли в систему!');
navigate('/'); // Перенаправляем на главную страницу
} catch (error) {
console.error('Error logging in:', error);
alert('Ошибка при входе в систему. Проверьте введенные данные.');
}
};
return (
<div className="login-page">
<h1>Login</h1>
<Form onFinish={onFinish}>
<Form.Item
label="Email"
name="email"
rules={[{ required: true, type: 'email', message: 'Пожалуйста, введите корректный email!' }]}
>
<Input value={email} onChange={e => setEmail(e.target.value)} />
</Form.Item>
<Form.Item
label="Password"
name="password"
rules={[{ required: true, min: 8, message: 'Пароль должен содержать не менее 8 символов!' }]}
>
<Input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Войти
</Button>
</Form.Item>
<p>
Нет аккаунта? <Link to="/register">Зарегистрируйтесь</Link>
</p>
</Form>
</div>
);
};
export default LoginPage;

View File

@ -0,0 +1,70 @@
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { Button, Form, Input } from 'antd';
import { createUser } from '../API/api';
import IRegisterRequest from '../Requests/RegisterRequest';
export function RegisterPage () {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const navigate = useNavigate();
const handleFinish = async () => {
// Create the user data object to send to the API
const userData: IRegisterRequest = {
name,
email,
password,
};
try {
if (!navigator.onLine) {
alert('No internet connection');
return;
}
const response = await createUser(userData); // Pass the userData object
if (response) {
navigate('/login'); // Redirect to the login page after successful registration
alert('Пользователь успешно зарегестрирован!');
} else {
console.error('Error registering:');
}
} catch (error) {
console.error('Error registering:', error);
alert('Error registering');
}
};
return (
<div className="register-page">
<h1>Register</h1>
<Form onFinish={handleFinish}>
<Form.Item label="Name" name="name" rules={[{ required: true, message: 'Пожалуйста, введите имя!' }]}>
<Input value={name} onChange={e => setName(e.target.value)} />
</Form.Item>
<Form.Item label="Email" name="email" rules={[{ required: true, type: 'email', message: 'Пожалуйста, введите корректный адрес почты!' }]}>
<Input value={email} onChange={e => setEmail(e.target.value)} />
</Form.Item>
<Form.Item label="Password" name="password" rules={[{ required: true, min: 8, message: 'Пароль должен содержать не менее 8 символов!' }]}>
<Input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Регистрация
</Button>
</Form.Item>
</Form>
<p>
Уже есть аккаунт? <Link to="/login">Войдите</Link>
</p>
</div>
);
};
export default RegisterPage;

1184
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +0,0 @@
{
"dependencies": {
"antd": "^5.21.6",
"react-router-dom": "^6.27.0"
},
"devDependencies": {
"@types/react": "^18.3.12"
}
}