From bc293f6370eafdf8af05cc0a137b5b1b9d4e72f2 Mon Sep 17 00:00:00 2001 From: Tatiana Nikolaeva Date: Sat, 10 May 2025 15:56:50 +0500 Subject: [PATCH] requests for auth/register --- SurveyFrontend/src/App.tsx | 6 +- SurveyFrontend/src/api/AuthApi.ts | 30 +++-- SurveyFrontend/src/api/BaseApi.ts | 35 +++++- SurveyFrontend/src/api/QuestionApi.ts | 29 +++++ SurveyFrontend/src/api/SurveyApi.ts | 117 ++++++++++++++++++ .../src/components/LoginForm/LoginForm.tsx | 38 +++++- .../components/MySurveyList/MySurveyList.tsx | 69 +++++++---- .../components/RegisterForm/RegisterForm.tsx | 62 ++++++++-- .../src/pages/AuthForm/AuthForm.tsx | 25 +++- 9 files changed, 348 insertions(+), 63 deletions(-) create mode 100644 SurveyFrontend/src/api/QuestionApi.ts create mode 100644 SurveyFrontend/src/api/SurveyApi.ts diff --git a/SurveyFrontend/src/App.tsx b/SurveyFrontend/src/App.tsx index a231648..fa8d8ba 100644 --- a/SurveyFrontend/src/App.tsx +++ b/SurveyFrontend/src/App.tsx @@ -6,14 +6,14 @@ import SettingSurvey from "./components/SettingSurvey/SettingSurvey.tsx"; import {MySurveysPage} from "./pages/MySurveysPage/MySurveysPage.tsx"; import {Results} from "./components/Results/Results.tsx"; import {MySurveyList} from "./components/MySurveyList/MySurveyList.tsx"; -import LoginForm from "./components/LoginForm/LoginForm.tsx"; import AuthForm from "./pages/AuthForm/AuthForm.tsx"; const App = () => { return( - } /> + } /> + } /> }> } /> @@ -31,8 +31,6 @@ const App = () => { } /> - } /> - } /> ); diff --git a/SurveyFrontend/src/api/AuthApi.ts b/SurveyFrontend/src/api/AuthApi.ts index 60e3a84..207da21 100644 --- a/SurveyFrontend/src/api/AuthApi.ts +++ b/SurveyFrontend/src/api/AuthApi.ts @@ -16,7 +16,13 @@ export const registerUser = async (data: IRegistrationData) => { const response = await fetch(`${BASE_URL}/auth/register`, { ...createRequestConfig('POST'), body: JSON.stringify(data), }) - return await handleResponse(response); + const responseData = await handleResponse(response); + + if (responseData.accessToken) { + localStorage.setItem("token", responseData.accessToken); + } + + return responseData; } catch (error){ console.error("Registration error:", error); throw error; @@ -24,14 +30,22 @@ export const registerUser = async (data: IRegistrationData) => { } export const authUser = async (data: IAuthData) => { - try{ + try { const response = await fetch(`${BASE_URL}/auth/login`, { - ...createRequestConfig('POST'), body: JSON.stringify(data), - }) - return await handleResponse(response); - } - catch(error){ + ...createRequestConfig('POST'), + body: JSON.stringify(data), + }); + const responseData = await handleResponse(response); + console.log("Полный ответ сервера:", responseData); // Добавьте эту строку + + const token = responseData.accessToken || responseData.token; + if (token) { + localStorage.setItem("token", token); + } + + return responseData; + } catch (error) { console.error("Login error:", error); throw error; } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/SurveyFrontend/src/api/BaseApi.ts b/SurveyFrontend/src/api/BaseApi.ts index 88861bb..3be4ee4 100644 --- a/SurveyFrontend/src/api/BaseApi.ts +++ b/SurveyFrontend/src/api/BaseApi.ts @@ -13,7 +13,8 @@ interface RequestConfig { * @returns Конфигурация для fetch-запроса */ const createRequestConfig = (method: string, isFormData: boolean = false): RequestConfig => { - const token = localStorage.getItem("accessToken"); + const token = localStorage.getItem("token"); + const config: RequestConfig = { method, headers: {}, @@ -37,14 +38,36 @@ const createRequestConfig = (method: string, isFormData: boolean = false): Reque * @param response Ответ от fetch * @returns Распарсенные данные или ошибку */ -const handleResponse = async (response: Response) => { - const data = await response.json(); +// const handleResponse = async (response: Response) => { +// const data = await response.json(); +// +// if (!response.ok) { +// throw new Error(data.message || "Произошла ошибка"); +// } +// +// return data; +// }; - if (!response.ok) { - throw new Error(data.message || "Произошла ошибка"); +const handleResponse = async (response: Response) => { + // Проверяем, есть ли контент в ответе + const responseText = await response.text(); + + if (!responseText) { + if (response.ok) { + return null; // Если ответ пустой, но статус 200, возвращаем null + } + throw new Error(`HTTP ${response.status}: ${response.statusText}`); } - return data; + try { + const data = JSON.parse(responseText); + if (!response.ok) { + throw new Error(data.message || `HTTP ${response.status}`); + } + return data; + } catch (e) { + throw new Error(`Не удалось разобрать ответ сервера: ${e}`); + } }; export { BASE_URL, createRequestConfig, handleResponse }; \ No newline at end of file diff --git a/SurveyFrontend/src/api/QuestionApi.ts b/SurveyFrontend/src/api/QuestionApi.ts new file mode 100644 index 0000000..ba3c2a8 --- /dev/null +++ b/SurveyFrontend/src/api/QuestionApi.ts @@ -0,0 +1,29 @@ +import {BASE_URL, createRequestConfig, handleResponse} from "./BaseApi.ts"; + +export interface INewQuestion{ + title: string; + questionType: string; + answerVariants: string[]; +} +// +// export interface IErrorQuestionResponse { +// +// } + +export const addNewQuestion = async (surveyId: number, question: INewQuestion) => { + const token = localStorage.getItem("token"); + if (!token) { + throw new Error("Токен отсутствует"); + } + + try{ + const response = await fetch(`${BASE_URL}/surveys/${surveyId}/questions`, { + ...createRequestConfig('POST'), + body: JSON.stringify(question), + }) + return await handleResponse(response) + } catch (error){ + throw new Error(`Error when adding a new question: ${error}`); + } +} + diff --git a/SurveyFrontend/src/api/SurveyApi.ts b/SurveyFrontend/src/api/SurveyApi.ts new file mode 100644 index 0000000..95b6032 --- /dev/null +++ b/SurveyFrontend/src/api/SurveyApi.ts @@ -0,0 +1,117 @@ +import {BASE_URL, createRequestConfig, handleResponse} from "./BaseApi.ts"; + +export interface ISurvey { + id: number; + title: string; + description: string; + createdBy: number; +} + +export interface INewSurvey{ + title: string; + description: string; +} + +/** + * getMySurveys - запрос на получение моих опросов + */ +export const getMySurveys = async (): Promise => { + const token = localStorage.getItem("token"); + if (!token) { + throw new Error("Токен отсутствует"); + } + + try { + const response = await fetch(`${BASE_URL}/surveys/my`, { + ...createRequestConfig('GET'), + }); + return await handleResponse(response); + } catch (error) { + console.error("Error receiving surveys:", error); + throw error; + } +} + +/** + * getAllSurvey - запрос на получение всех опросов + */ +export const getAllSurveys = async (): Promise => { + try{ + const response = await fetch(`${BASE_URL}/surveys`, { + ...createRequestConfig('GET'), + }) + return await handleResponse(response); + } catch (error) { + console.error("Error receiving surveys:", error); + throw error; + } +} + +/** + * postNewSurvey - добавление нового опроса + * @param survey + */ +export const postNewSurvey = async (survey: INewSurvey): Promise => { + const token = localStorage.getItem("token"); + if (!token) { + throw new Error("Токен отсутствует"); + } + + try{ + const response = await fetch(`${BASE_URL}/surveys`, { + ...createRequestConfig('POST'), + body: JSON.stringify(survey) + }) + + // return await handleResponse(response); + + if (response.status === 201) { + return await handleResponse(response); + } + throw new Error(`Ожидался код 201, получен ${response.status}`); + } catch (error) { + console.error(`Error when adding a new survey: ${error}`); + throw error; + } +} + +/** + * Запрос на получение опроса по заданному ID + * @param surveyId - ID опроса + */ +export const getSurveyById = async (surveyId: number): Promise => { + try{ + const response = await fetch(`${BASE_URL}/surveys/${surveyId}`, { + ...createRequestConfig('GET'), + }) + return await handleResponse(response); + } catch (error){ + console.error(`Error finding the survey by id: ${error}`); + throw error; + } +} + +/** + * Запрос на удаление опроса + * @param surveyId - ID выбранного опроса + */ +export const deleteSurvey = async (surveyId: string) => { + const token = localStorage.getItem("token"); + if (!token) { + throw new Error("Токен отсутствует"); + } + + try{ + const response = await fetch(`${BASE_URL}/surveys/${surveyId}`, { + ...createRequestConfig('DELETE'), + }) + const responseData = await handleResponse(response); + if (response.ok && !responseData){ + return {success: true}; + } + return responseData; + } catch (error){ + console.error(`Error deleting a survey: ${error}`); + throw error; + } +} \ No newline at end of file diff --git a/SurveyFrontend/src/components/LoginForm/LoginForm.tsx b/SurveyFrontend/src/components/LoginForm/LoginForm.tsx index b560c2c..553cd37 100644 --- a/SurveyFrontend/src/components/LoginForm/LoginForm.tsx +++ b/SurveyFrontend/src/components/LoginForm/LoginForm.tsx @@ -1,20 +1,45 @@ -import { Link } from "react-router-dom"; +import {Link, useNavigate} from "react-router-dom"; import styles from './LoginForm.module.css'; -import { useState } from 'react'; +import {useRef, useState} from 'react'; +import {authUser} from "../../api/AuthApi.ts"; -const RegisterForm = () => { +const LoginForm = () => { const [focused, setFocused] = useState({ email: false, password: false }); + + const navigate = useNavigate(); + + const emailRef = useRef(null); // ref для поля email + const passwordRef = useRef(null); // ref для поля password + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + const email = emailRef.current?.value || ''; + const password = passwordRef.current?.value || ''; + + try{ + const responseData = await authUser({email, password}); + if (responseData && !responseData.error) + navigate('/my-surveys'); + else + console.error('Ошибка аутентификации:', responseData); + } + catch(err){ + console.error('Ошибка при отправке запроса:', err); + } + } + return (

С возвращением!

-
+ setFocused({ ...focused, email: true })} onBlur={() => setFocused({ ...focused, email: false })} style={{ color: focused.email ? 'black' : 'inherit' }} @@ -23,11 +48,12 @@ const RegisterForm = () => { className={`${styles.input} ${styles.password}`} type='password' placeholder='Пароль' + ref={passwordRef} onFocus={() => setFocused({ ...focused, password: true })} onBlur={() => setFocused({ ...focused, password: false })} style={{ color: focused.password ? 'black' : 'inherit' }} /> - +

Еще не с нами? Зарегистрируйтесь! @@ -36,4 +62,4 @@ const RegisterForm = () => { ); } -export default RegisterForm; +export default LoginForm; diff --git a/SurveyFrontend/src/components/MySurveyList/MySurveyList.tsx b/SurveyFrontend/src/components/MySurveyList/MySurveyList.tsx index 678d30d..210996e 100644 --- a/SurveyFrontend/src/components/MySurveyList/MySurveyList.tsx +++ b/SurveyFrontend/src/components/MySurveyList/MySurveyList.tsx @@ -1,35 +1,58 @@ import styles from './MySurveysList.module.css' import {useNavigate} from "react-router-dom"; +import {useEffect, useState} from "react"; +import {getMySurveys, ISurvey} from "../../api/SurveyApi.ts"; -interface MySurveyItem{ - id: string, - title: string, - description: string, - date: string + +interface MySurveyItem extends ISurvey{ status: 'active' | 'completed' } export const MySurveyList = () => { const navigate = useNavigate(); + const [surveys, setSurveys] = useState([]); - const surveys: MySurveyItem[] = [ - { - id: '1', - title: 'Опрос 1', - description: 'Описание опроса 1', - date: '27-04-2025', - status: 'active', - }, - { - id: '2', - title: 'Опрос 2', - description: 'Описание опроса 2', - date: '01-01-2025', - status: 'completed', - } - ] + useEffect(() => { + const fetchSurvey = async () => { + try { + const mySurveys = await getMySurveys(); + const surveysWithStatus: MySurveyItem[] = mySurveys.map((survey: ISurvey) => ({ + ...survey, + status: 'active', + })); + setSurveys(surveysWithStatus); + } catch (error) { + console.error('Ошибка при получении списка опросов:', error); - const handleSurveyClick = (id: string) => { + if (error instanceof Error && error.message.includes("401")) { + // Если ошибка 401, перенаправляем на страницу входа + navigate('/login'); + } else { + alert("Ошибка при загрузке опросов: " + (error instanceof Error && error.message)); + } + } + }; + fetchSurvey(); + }, [navigate]); + + // const surveys: MySurveyItem[] = [ + // { + // id: '1', + // title: 'Опрос 1', + // description: 'Описание опроса 1', + // createdBy: '27-04-2025', + // status: 'active', + // }, + // { + // id: '2', + // title: 'Опрос 2', + // description: 'Описание опроса 2', + // createdBy: '01-01-2025', + // status: 'completed', + // } + // ] + + const handleSurveyClick = (id: number) => { navigate(`/survey/${id}/questions`) } @@ -46,7 +69,7 @@ export const MySurveyList = () => {

{survey.title}

{survey.description}

- Дата создания: {survey.date} + Дата создания: {survey.createdBy}
{ const [focused, setFocused] = useState({ - name: false, - surname: false, + firstName: false, + lastName: false, email: false, password: false }); + + const nameRef = useRef(null); + const surnameRef = useRef(null); + const emailRef = useRef(null); + const passwordRef = useRef(null); + + const navigate = useNavigate(); + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + const firstName = nameRef.current?.value || ''; + const lastName = surnameRef.current?.value || ''; + const email = emailRef.current?.value || ''; + const password = passwordRef.current?.value || ''; + const username = firstName + lastName || ''; + + try{ + const responseData = await registerUser({username, firstName, lastName, email, password}); + console.log(responseData); //проверка вывода данных + if (responseData && !responseData.error) { + console.log('Регистрация успешна') + navigate('/my-surveys'); + } + else { + console.error(`Ошибка регистрации: ${responseData}`); + console.log('Регистраиця не удалась'); + } + } + catch (err) { + console.error(`Ошибка при отправке запроса ${err}`); + } + } + return (

Регистрация

-
+ setFocused({ ...focused, name: true })} - onBlur={() => setFocused({ ...focused, name: false })} - style={{ color: focused.name ? 'black' : 'inherit' }} + ref={nameRef} + onFocus={() => setFocused({ ...focused, firstName: true })} + onBlur={() => setFocused({ ...focused, firstName: false })} + style={{ color: focused.firstName ? 'black' : 'inherit' }} /> setFocused({ ...focused, surname: true })} - onBlur={() => setFocused({ ...focused, surname: false })} - style={{ color: focused.surname ? 'black' : 'inherit' }} + ref={surnameRef} + onFocus={() => setFocused({ ...focused, lastName: true })} + onBlur={() => setFocused({ ...focused, lastName: false })} + style={{ color: focused.lastName ? 'black' : 'inherit' }} /> setFocused({ ...focused, email: true })} onBlur={() => setFocused({ ...focused, email: false })} style={{ color: focused.email ? 'black' : 'inherit' }} @@ -41,11 +78,12 @@ const RegisterForm = () => { className={`${styles.input} ${styles.password}`} type='password' placeholder='Пароль' + ref={passwordRef} onFocus={() => setFocused({ ...focused, password: true })} onBlur={() => setFocused({ ...focused, password: false })} style={{ color: focused.password ? 'black' : 'inherit' }} /> - +

Уже с нами? Войдите! diff --git a/SurveyFrontend/src/pages/AuthForm/AuthForm.tsx b/SurveyFrontend/src/pages/AuthForm/AuthForm.tsx index 9f4e5d4..7d52d08 100644 --- a/SurveyFrontend/src/pages/AuthForm/AuthForm.tsx +++ b/SurveyFrontend/src/pages/AuthForm/AuthForm.tsx @@ -5,13 +5,30 @@ import {useLocation} from "react-router-dom"; const AuthForm = () => { + // const location = useLocation(); + // const isLogin = location.pathname === '/login'; + // + // return ( + //

+ // {isLogin ? : } + //
+ // ); + const location = useLocation(); - const isLogin = location.pathname === '/login'; + const isLoginPage = location.pathname === '/login'; + const isRegisterPage = location.pathname === '/register'; + + let content; + if (isLoginPage) { + content = ; + } else if (isRegisterPage) { + content = ; + } else { + content = ; // По умолчанию показываем LoginForm + } return ( -
- {isLogin ? : } -
+
{content}
); }