From 9a3f05ef60065f1aacc8f34460d07b80b9cf4a1b Mon Sep 17 00:00:00 2001 From: Tatiana Nikolaeva Date: Thu, 8 May 2025 18:22:44 +0500 Subject: [PATCH 1/4] add components register and login --- SurveyFrontend/src/App.tsx | 10 ++- .../components/LoginForm/LoginForm.module.css | 85 +++++++++++++++++++ .../src/components/LoginForm/LoginForm.tsx | 39 +++++++++ .../RegisterForm/RegisterForm.module.css | 84 ++++++++++++++++++ .../components/RegisterForm/RegisterForm.tsx | 57 +++++++++++++ .../src/pages/AuthForm/AuthForm.module.css | 12 +++ .../src/pages/AuthForm/AuthForm.tsx | 18 ++++ 7 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 SurveyFrontend/src/components/LoginForm/LoginForm.module.css create mode 100644 SurveyFrontend/src/components/LoginForm/LoginForm.tsx create mode 100644 SurveyFrontend/src/components/RegisterForm/RegisterForm.module.css create mode 100644 SurveyFrontend/src/components/RegisterForm/RegisterForm.tsx create mode 100644 SurveyFrontend/src/pages/AuthForm/AuthForm.module.css create mode 100644 SurveyFrontend/src/pages/AuthForm/AuthForm.tsx diff --git a/SurveyFrontend/src/App.tsx b/SurveyFrontend/src/App.tsx index 8acf6f3..a231648 100644 --- a/SurveyFrontend/src/App.tsx +++ b/SurveyFrontend/src/App.tsx @@ -1,17 +1,19 @@ import './App.css' -import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom"; +import {BrowserRouter, Route, Routes} from "react-router-dom"; import {SurveyCreateAndEditingPage} from "./pages/SurveyCreateAndEditingPage/SurveyCreateAndEditingPage.tsx"; import Survey from "./components/Survey/Survey.tsx"; 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( - } /> + } /> }> } /> @@ -27,6 +29,10 @@ const App = () => { } /> } /> + + } /> + } /> + } /> ); diff --git a/SurveyFrontend/src/components/LoginForm/LoginForm.module.css b/SurveyFrontend/src/components/LoginForm/LoginForm.module.css new file mode 100644 index 0000000..fca2331 --- /dev/null +++ b/SurveyFrontend/src/components/LoginForm/LoginForm.module.css @@ -0,0 +1,85 @@ +.loginContainer{ + width: 31%; + background-color: #FFFFFF; + padding: 42.5px 65px; + margin: auto; + border-radius: 43px; + margin-bottom: 0; +} + +.title{ + text-align: center; + font-weight: 600; + font-size: 40px; + line-height: 88%; + padding: 0; + margin-bottom: 80px; + margin-top: 0; +} + +.form{ + text-align: center; + display: flex; + flex-direction: column; + gap: 80px; + margin-bottom: 80px; +} + +.input { + font-size: 24px; + font-weight: 600; + line-height: 88%; + color: #000000; /* Цвет текста по умолчанию */ + outline: none; + border: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.2); /* Нижняя граница с прозрачностью */ + padding: 5px 0; + opacity: 1; /* Установите opacity в 1 для input, а для placeholder используйте opacity */ +} + +.input::placeholder { + font-size: 24px; + font-weight: 600; + line-height: 88%; + color: #000000; + opacity: 0.2; /* Прозрачность placeholder */ +} + +.input:focus::placeholder { + opacity: 0; /* Убираем placeholder при фокусе */ +} + +/* Отключаем стиль для input, когда в нём есть данные */ +.input:not(:placeholder-shown) { + color: black; + opacity: 1; +} + +.input:focus { + border-bottom: 1px solid black; /* Чёрная граница при фокусе */ +} + +.signIn{ + margin: auto; + padding: 26.5px 67px; + width: fit-content; + border-radius: 24px; + background-color: #3788D6; + color: #FFFFFF; + font-size: 24px; + font-weight: 600; + line-height: 120%; + border: none; + outline: none; +} + +.recommendation{ + text-align: center; + font-size: 18px; + font-weight: 500; +} + +.recommendationLink{ + color: #3788D6; + margin-left: 5px; +} \ No newline at end of file diff --git a/SurveyFrontend/src/components/LoginForm/LoginForm.tsx b/SurveyFrontend/src/components/LoginForm/LoginForm.tsx new file mode 100644 index 0000000..b560c2c --- /dev/null +++ b/SurveyFrontend/src/components/LoginForm/LoginForm.tsx @@ -0,0 +1,39 @@ +import { Link } from "react-router-dom"; +import styles from './LoginForm.module.css'; +import { useState } from 'react'; + +const RegisterForm = () => { + const [focused, setFocused] = useState({ + email: false, + password: false + }); + return ( +
+

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

+
+ setFocused({ ...focused, email: true })} + onBlur={() => setFocused({ ...focused, email: false })} + style={{ color: focused.email ? 'black' : 'inherit' }} + /> + setFocused({ ...focused, password: true })} + onBlur={() => setFocused({ ...focused, password: false })} + style={{ color: focused.password ? 'black' : 'inherit' }} + /> + +
+

Еще не с нами? + Зарегистрируйтесь! +

+
+ ); +} + +export default RegisterForm; diff --git a/SurveyFrontend/src/components/RegisterForm/RegisterForm.module.css b/SurveyFrontend/src/components/RegisterForm/RegisterForm.module.css new file mode 100644 index 0000000..d2ac3b1 --- /dev/null +++ b/SurveyFrontend/src/components/RegisterForm/RegisterForm.module.css @@ -0,0 +1,84 @@ +.registerContainer{ + width: 31%; + background-color: #FFFFFF; + padding: 94px 80px; + margin: auto; + border-radius: 43px; +} + +.title{ + text-align: center; + font-weight: 600; + font-size: 40px; + line-height: 88%; + padding: 0; + margin-bottom: 80px; + margin-top: 0; +} + +.form{ + text-align: center; + display: flex; + flex-direction: column; + gap: 80px; + margin-bottom: 80px; +} + +.input { + font-size: 24px; + font-weight: 600; + line-height: 88%; + color: #000000; /* Цвет текста по умолчанию */ + outline: none; + border: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.2); /* Нижняя граница с прозрачностью */ + padding: 5px 0; + opacity: 1; /* Установите opacity в 1 для input, а для placeholder используйте opacity */ +} + +.input::placeholder { + font-size: 24px; + font-weight: 600; + line-height: 88%; + color: #000000; + opacity: 0.2; /* Прозрачность placeholder */ +} + +.input:focus::placeholder { + opacity: 0; /* Убираем placeholder при фокусе */ +} + +/* Отключаем стиль для input, когда в нём есть данные */ +.input:not(:placeholder-shown) { + color: black; + opacity: 1; +} + +.input:focus { + border-bottom: 1px solid black; /* Чёрная граница при фокусе */ +} + +.signUp{ + margin: auto; + padding: 25.5px 16px; + width: fit-content; + border-radius: 24px; + background-color: #3788D6; + color: #FFFFFF; + font-size: 24px; + font-weight: 600; + line-height: 120%; + border: none; + outline: none; +} + +.recommendation{ + text-align: center; + font-size: 18px; + font-weight: 500; +} + +.recommendationLink{ + color: #3788D6; + margin-left: 5px; +} \ No newline at end of file diff --git a/SurveyFrontend/src/components/RegisterForm/RegisterForm.tsx b/SurveyFrontend/src/components/RegisterForm/RegisterForm.tsx new file mode 100644 index 0000000..6cb0238 --- /dev/null +++ b/SurveyFrontend/src/components/RegisterForm/RegisterForm.tsx @@ -0,0 +1,57 @@ +import { Link } from "react-router-dom"; +import styles from './RegisterForm.module.css'; +import { useState } from 'react'; + +const RegisterForm = () => { + const [focused, setFocused] = useState({ + name: false, + surname: false, + email: false, + password: false + }); + return ( +
+

Регистрация

+
+ setFocused({ ...focused, name: true })} + onBlur={() => setFocused({ ...focused, name: false })} + style={{ color: focused.name ? 'black' : 'inherit' }} + /> + setFocused({ ...focused, surname: true })} + onBlur={() => setFocused({ ...focused, surname: false })} + style={{ color: focused.surname ? 'black' : 'inherit' }} + /> + setFocused({ ...focused, email: true })} + onBlur={() => setFocused({ ...focused, email: false })} + style={{ color: focused.email ? 'black' : 'inherit' }} + /> + setFocused({ ...focused, password: true })} + onBlur={() => setFocused({ ...focused, password: false })} + style={{ color: focused.password ? 'black' : 'inherit' }} + /> + +
+

Уже с нами? + Войдите! +

+
+ ); +} + +export default RegisterForm; diff --git a/SurveyFrontend/src/pages/AuthForm/AuthForm.module.css b/SurveyFrontend/src/pages/AuthForm/AuthForm.module.css new file mode 100644 index 0000000..bae0307 --- /dev/null +++ b/SurveyFrontend/src/pages/AuthForm/AuthForm.module.css @@ -0,0 +1,12 @@ +.page{ + width: 100%; + min-height: 100vh; + background-color: #F6F6F6; + padding: 61.5px 0; +} + +.pageLogin{ + width: 100%; + background-color: #F6F6F6; + padding: 157px 0; +} \ No newline at end of file diff --git a/SurveyFrontend/src/pages/AuthForm/AuthForm.tsx b/SurveyFrontend/src/pages/AuthForm/AuthForm.tsx new file mode 100644 index 0000000..9f4e5d4 --- /dev/null +++ b/SurveyFrontend/src/pages/AuthForm/AuthForm.tsx @@ -0,0 +1,18 @@ +import styles from './AuthForm.module.css'; +import LoginForm from "../../components/LoginForm/LoginForm.tsx"; +import RegisterForm from "../../components/RegisterForm/RegisterForm.tsx"; +import {useLocation} from "react-router-dom"; + + +const AuthForm = () => { + const location = useLocation(); + const isLogin = location.pathname === '/login'; + + return ( +
+ {isLogin ? : } +
+ ); +} + +export default AuthForm; \ No newline at end of file From bc293f6370eafdf8af05cc0a137b5b1b9d4e72f2 Mon Sep 17 00:00:00 2001 From: Tatiana Nikolaeva Date: Sat, 10 May 2025 15:56:50 +0500 Subject: [PATCH 2/4] 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}
); } From fa5fe30c34753b61cfdad3621a85bee1c8e7af78 Mon Sep 17 00:00:00 2001 From: Tatiana Nikolaeva Date: Wed, 14 May 2025 17:59:36 +0500 Subject: [PATCH 3/4] correction of working capacity --- SurveyFrontend/src/api/AuthApi.ts | 12 ++++ .../AnswerOption/AnswerOption.module.css | 49 +++++++++---- .../components/AnswerOption/AnswerOption.tsx | 35 +++++++--- .../src/components/Header/Header.tsx | 14 ++-- SurveyFrontend/src/components/Logo/Logo.tsx | 5 +- .../QuestionItem/QuestionItem.module.css | 3 + .../components/QuestionItem/QuestionItem.tsx | 8 +++ .../components/RegisterForm/RegisterForm.tsx | 6 +- .../SettingSurvey/SettingSurvey.module.css | 1 + .../SurveyInfo/SurveyInfo.module.css | 60 +++++++++------- .../src/components/SurveyInfo/SurveyInfo.tsx | 69 ++++++++++++------- 11 files changed, 180 insertions(+), 82 deletions(-) diff --git a/SurveyFrontend/src/api/AuthApi.ts b/SurveyFrontend/src/api/AuthApi.ts index 207da21..2855bad 100644 --- a/SurveyFrontend/src/api/AuthApi.ts +++ b/SurveyFrontend/src/api/AuthApi.ts @@ -20,6 +20,10 @@ export const registerUser = async (data: IRegistrationData) => { if (responseData.accessToken) { localStorage.setItem("token", responseData.accessToken); + localStorage.setItem("user", JSON.stringify({ + firstName: data.firstName, + lastName: data.lastName + })); } return responseData; @@ -41,6 +45,14 @@ export const authUser = async (data: IAuthData) => { const token = responseData.accessToken || responseData.token; if (token) { localStorage.setItem("token", token); + const user = localStorage.getItem("user"); + if (!responseData.user && user) { + responseData.user = JSON.parse(user); + } + + if (responseData.user) { + localStorage.setItem("user", JSON.stringify(responseData.user)); + } } return responseData; diff --git a/SurveyFrontend/src/components/AnswerOption/AnswerOption.module.css b/SurveyFrontend/src/components/AnswerOption/AnswerOption.module.css index 6a0ec90..9f0ce82 100644 --- a/SurveyFrontend/src/components/AnswerOption/AnswerOption.module.css +++ b/SurveyFrontend/src/components/AnswerOption/AnswerOption.module.css @@ -1,46 +1,67 @@ -/*AnswerOption.module.css*/ +/*!*AnswerOption.module.css*!*/ -.answer{ +.answer { width: 100%; display: flex; gap: 10px; margin-bottom: 17px; + align-items: flex-start; } -.textAnswer{ +.textAnswer { text-align: left; border: none; - background-color: #ffffff; + background: none; font-size: 18px; font-weight: 500; word-break: break-word; width: 70%; + padding: 0; + line-height: 24px; + cursor: text; + margin-top: 2px; } -.buttonMarker{ +.buttonMarker { padding: 0; border: none; - background-color: transparent; + background: none; + position: relative; + top: 0; + transition: top 0.1s ease; + cursor: pointer; + height: 24px; } -.answerIcon{ - vertical-align: middle; +.buttonMarker.editing { + top: 3px; +} + +.answerIcon { width: 24px; + height: 24px; + display: block; } -.answerInput{ - vertical-align: middle; +.answerInput { font-size: 18px; font-weight: 500; outline: none; border: none; resize: none; - display: flex; - align-items: center; - box-sizing: border-box; + width: 70%; + padding: 0; + margin-top: 2px; + font-family: inherit; + min-height: 24px; + height: auto; + overflow-y: hidden; + line-height: 1.5; + white-space: pre-wrap; + word-wrap: break-word; } -.deleteButton{ +.deleteButton { margin-left: auto; border: none; background-color: transparent; diff --git a/SurveyFrontend/src/components/AnswerOption/AnswerOption.tsx b/SurveyFrontend/src/components/AnswerOption/AnswerOption.tsx index e0c78b1..c08c213 100644 --- a/SurveyFrontend/src/components/AnswerOption/AnswerOption.tsx +++ b/SurveyFrontend/src/components/AnswerOption/AnswerOption.tsx @@ -32,8 +32,22 @@ const AnswerOption: React.FC = ({index, value, onChange, onDe const handleTextareaChange = (event: React.ChangeEvent) => { setCurrentValue(event.target.value); + // Автоматическое изменение высоты + if (textAreaRef.current) { + textAreaRef.current.style.height = 'auto'; + textAreaRef.current.style.height = `${textAreaRef.current.scrollHeight}px`; + } }; + useEffect(() => { + if (isEditing && textAreaRef.current) { + textAreaRef.current.focus(); + // Установка начальной высоты + textAreaRef.current.style.height = 'auto'; + textAreaRef.current.style.height = `${textAreaRef.current.scrollHeight}px`; + } + }, [isEditing]); + const handleSave = () => { setIsEditing(false); onChange(currentValue); @@ -66,7 +80,10 @@ const AnswerOption: React.FC = ({index, value, onChange, onDe return (
- {isEditing ? ( -