diff --git a/SurveyFrontend/src/App.tsx b/SurveyFrontend/src/App.tsx index 0ea3b92..8acf6f3 100644 --- a/SurveyFrontend/src/App.tsx +++ b/SurveyFrontend/src/App.tsx @@ -1,19 +1,32 @@ import './App.css' import {BrowserRouter, Navigate, Route, Routes} from "react-router-dom"; -import {SurveyEditingPage} from "./pages/SurveyEditingPage/SurveyEditingPage.tsx"; +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"; const App = () => { return( - } /> + } /> - }> + }> } /> } /> + + }> + } /> + + + }> + } /> + } /> + } /> + ); diff --git a/SurveyFrontend/src/api/AuthApi.ts b/SurveyFrontend/src/api/AuthApi.ts new file mode 100644 index 0000000..60e3a84 --- /dev/null +++ b/SurveyFrontend/src/api/AuthApi.ts @@ -0,0 +1,37 @@ +import {BASE_URL, createRequestConfig, handleResponse} from "./BaseApi.ts"; + +interface IAuthData{ + email: string; + password: string; +} + +interface IRegistrationData extends IAuthData{ + username: string; + firstName: string; + lastName: string; +} + +export const registerUser = async (data: IRegistrationData) => { + try{ + const response = await fetch(`${BASE_URL}/auth/register`, { + ...createRequestConfig('POST'), body: JSON.stringify(data), + }) + return await handleResponse(response); + } catch (error){ + console.error("Registration error:", error); + throw error; + } +} + +export const authUser = async (data: IAuthData) => { + try{ + const response = await fetch(`${BASE_URL}/auth/login`, { + ...createRequestConfig('POST'), body: JSON.stringify(data), + }) + return await handleResponse(response); + } + catch(error){ + console.error("Login error:", error); + throw error; + } +} \ No newline at end of file diff --git a/SurveyFrontend/src/api/BaseApi.ts b/SurveyFrontend/src/api/BaseApi.ts new file mode 100644 index 0000000..88861bb --- /dev/null +++ b/SurveyFrontend/src/api/BaseApi.ts @@ -0,0 +1,50 @@ +const BASE_URL = "https://survey.slavagm.ru/api"; + +interface RequestConfig { + method: string; + headers: Record; + body?: BodyInit | null; +} + +/** + * Создаёт конфигурацию для fetch-запроса + * @param method HTTP-метод (GET, POST, PUT, DELETE) + * @param isFormData Флаг, указывающий, что отправляется FormData + * @returns Конфигурация для fetch-запроса + */ +const createRequestConfig = (method: string, isFormData: boolean = false): RequestConfig => { + const token = localStorage.getItem("accessToken"); + const config: RequestConfig = { + method, + headers: {}, + }; + + // Добавляем заголовок авторизации, если есть токен + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + + // Добавляем Content-Type, если это не FormData + if (!isFormData) { + config.headers["Content-Type"] = "application/json"; + } + + return config; +}; + +/** + * Обрабатывает ответ от сервера + * @param response Ответ от fetch + * @returns Распарсенные данные или ошибку + */ +const handleResponse = async (response: Response) => { + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message || "Произошла ошибка"); + } + + return data; +}; + +export { BASE_URL, createRequestConfig, handleResponse }; \ No newline at end of file diff --git a/SurveyFrontend/src/components/AddQuestionButton/AddQuestionButton.module.css b/SurveyFrontend/src/components/AddQuestionButton/AddQuestionButton.module.css index aa8331c..7326604 100644 --- a/SurveyFrontend/src/components/AddQuestionButton/AddQuestionButton.module.css +++ b/SurveyFrontend/src/components/AddQuestionButton/AddQuestionButton.module.css @@ -1,7 +1,6 @@ /*AddQuestionButton.module.css*/ .questionButton{ - display: block; margin: 0 auto; display: flex; flex-direction: column; diff --git a/SurveyFrontend/src/components/Header/Header.tsx b/SurveyFrontend/src/components/Header/Header.tsx index 06ddfd2..03be8fc 100644 --- a/SurveyFrontend/src/components/Header/Header.tsx +++ b/SurveyFrontend/src/components/Header/Header.tsx @@ -8,22 +8,23 @@ import {Link, useLocation} from "react-router-dom"; const Header: React.FC = () => { const location = useLocation(); - const isCreateSurveyActive = location.pathname.includes('/survey/edit'); - const isMySurveyActive = location.pathname === '/my-surveys'; + const isCreateSurveyActive = location.pathname.includes('/survey/create'); + const isSurveyPage = location.pathname.includes('/survey/') && !location.pathname.includes('/survey/create'); + const isMySurveysPage = location.pathname === '/my-surveys' || isSurveyPage; return (
{ + const navigate = useNavigate(); + + 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', + } + ] + + const handleSurveyClick = (id: string) => { + navigate(`/survey/${id}/questions`) + } + + return( +
+ {surveys.map((survey) => ( +
handleSurveyClick(survey.id)} + role="button" + tabIndex={0} + > +
+

{survey.title}

+

{survey.description}

+ Дата создания: {survey.date} +
+
+ {survey.status === 'active' ? 'Активен' : 'Завершён'} +
+
+ ))} +
+ ) +} \ No newline at end of file diff --git a/SurveyFrontend/src/components/MySurveyList/MySurveysList.module.css b/SurveyFrontend/src/components/MySurveyList/MySurveysList.module.css new file mode 100644 index 0000000..e623c4e --- /dev/null +++ b/SurveyFrontend/src/components/MySurveyList/MySurveysList.module.css @@ -0,0 +1,34 @@ +.main{ + background-color: #F6F6F6; + width: 100%; + height: 100vh; + padding: 34px 8%; +} + +.survey{ + display: flex; + justify-content: space-between; + background-color: #FFFFFF; + width: 79%; + border-radius: 14px; + padding: 29px 36px 29px 54px; + margin-bottom: 23px; +} + +.completed{ + width: fit-content; + height: fit-content; + padding: 15px 47px; + border-radius: 15px; + background-color: #65B953; + color: #FFFFFF; +} + +.active{ + width: fit-content; + height: fit-content; + padding: 15px 47px; + border-radius: 15px; + background-color: #B0B0B0; + color: #FFFFFF; +} \ No newline at end of file diff --git a/SurveyFrontend/src/components/Navigation/Navigation.tsx b/SurveyFrontend/src/components/Navigation/Navigation.tsx index d1261ee..1a97ae9 100644 --- a/SurveyFrontend/src/components/Navigation/Navigation.tsx +++ b/SurveyFrontend/src/components/Navigation/Navigation.tsx @@ -4,22 +4,28 @@ import styles from './Navigation.module.css' import NavigationItem from "../NavigationItem/NavigationItem.tsx"; import SaveButton from "../SaveButton/SaveButton.tsx"; - const Navigation: React.FC = () => { const location = useLocation(); const navigate = useNavigate(); - const activePage = location.pathname.split('/').pop() || 'questions' + const isSurveyPage = /\/survey\/[^/]+/.test(location.pathname); + const isNotCreateSurvey = !location.pathname.includes('/survey/create'); + const isMySurveysPage = isSurveyPage && isNotCreateSurvey; - const items = [ + const activePage = location.pathname.split('/').pop() ?? 'questions'; + + const baseItems = [ {id: 'questions', title: 'Вопросы'}, - {id: 'settings', title: 'Настройки'}, - {id: 'results', title: 'Результаты'} - ] + {id: 'settings', title: 'Настройки'} + ]; - const handleNavigationClick = (padeId: string) => { - navigate(`${padeId}`); - } + const items = isMySurveysPage + ? [...baseItems, {id: 'results', title: 'Результаты'}] + : baseItems; + + const handleNavigationClick = (pageId: string) => { + navigate(`${pageId}`, { relative: 'path' }); + }; return (
@@ -35,7 +41,6 @@ const Navigation: React.FC = () => { ))} -
); diff --git a/SurveyFrontend/src/components/Results/Results.module.css b/SurveyFrontend/src/components/Results/Results.module.css new file mode 100644 index 0000000..5483bb4 --- /dev/null +++ b/SurveyFrontend/src/components/Results/Results.module.css @@ -0,0 +1,5 @@ +/*Results.module.css*/ + +.results{ + width: 85%; +} \ No newline at end of file diff --git a/SurveyFrontend/src/components/Results/Results.tsx b/SurveyFrontend/src/components/Results/Results.tsx new file mode 100644 index 0000000..d21ab44 --- /dev/null +++ b/SurveyFrontend/src/components/Results/Results.tsx @@ -0,0 +1,10 @@ +import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx"; +import styles from './Results.module.css' + +export const Results = () => { + return( +
+ +
+ ) +} \ No newline at end of file diff --git a/SurveyFrontend/src/components/SurveyInfo/SurveyInfo.module.css b/SurveyFrontend/src/components/SurveyInfo/SurveyInfo.module.css index f6e8c7c..f80ffbd 100644 --- a/SurveyFrontend/src/components/SurveyInfo/SurveyInfo.module.css +++ b/SurveyFrontend/src/components/SurveyInfo/SurveyInfo.module.css @@ -13,7 +13,7 @@ .info{ min-width: 373px; display: block; - padding: 35px; /*подумать нужно ли справа слева отступы*/ + padding: 35px; } .titleSurvey{ diff --git a/SurveyFrontend/src/components/TypeDropdown/TypeDropdown.module.css b/SurveyFrontend/src/components/TypeDropdown/TypeDropdown.module.css index 33af9c7..dd347b2 100644 --- a/SurveyFrontend/src/components/TypeDropdown/TypeDropdown.module.css +++ b/SurveyFrontend/src/components/TypeDropdown/TypeDropdown.module.css @@ -4,7 +4,6 @@ width: 23%; position: relative; display: inline-block; - /*margin-right: 29px;*/ margin-left: auto; } diff --git a/SurveyFrontend/src/pages/SurveyEditingPage/SurveyEditingPage.module.css b/SurveyFrontend/src/pages/MySurveysPage/MySurveysPage.module.css similarity index 84% rename from SurveyFrontend/src/pages/SurveyEditingPage/SurveyEditingPage.module.css rename to SurveyFrontend/src/pages/MySurveysPage/MySurveysPage.module.css index ce16b39..1e2ee9a 100644 --- a/SurveyFrontend/src/pages/SurveyEditingPage/SurveyEditingPage.module.css +++ b/SurveyFrontend/src/pages/MySurveysPage/MySurveysPage.module.css @@ -1,4 +1,4 @@ -/*SurveyEditingPage.module.css*/ +/*MySurveysPage.module.css*/ .layout{ width: 100%; diff --git a/SurveyFrontend/src/pages/MySurveysPage/MySurveysPage.tsx b/SurveyFrontend/src/pages/MySurveysPage/MySurveysPage.tsx new file mode 100644 index 0000000..4b036aa --- /dev/null +++ b/SurveyFrontend/src/pages/MySurveysPage/MySurveysPage.tsx @@ -0,0 +1,12 @@ +import styles from "./MySurveysPage.module.css"; +import {MySurveyList} from "../../components/MySurveyList/MySurveyList.tsx"; +import Header from "../../components/Header/Header.tsx"; + +export const MySurveysPage = () => { + return ( +
+
+ +
+ ) +} \ No newline at end of file diff --git a/SurveyFrontend/src/pages/SurveyCreateAndEditingPage/SurveyCreateAndEditingPage.module.css b/SurveyFrontend/src/pages/SurveyCreateAndEditingPage/SurveyCreateAndEditingPage.module.css new file mode 100644 index 0000000..bb44365 --- /dev/null +++ b/SurveyFrontend/src/pages/SurveyCreateAndEditingPage/SurveyCreateAndEditingPage.module.css @@ -0,0 +1,17 @@ +/*SurveyCreateAndEditingPage.module.css*/ + +.layout{ + width: 100%; +} + +.main{ + width: 100%; + min-height: 85vh; + display: flex; + background-color: #F6F6F6; +} + +.content{ + width: 100%; + margin-left: 8.9%; +} \ No newline at end of file diff --git a/SurveyFrontend/src/pages/SurveyEditingPage/SurveyEditingPage.tsx b/SurveyFrontend/src/pages/SurveyCreateAndEditingPage/SurveyCreateAndEditingPage.tsx similarity index 80% rename from SurveyFrontend/src/pages/SurveyEditingPage/SurveyEditingPage.tsx rename to SurveyFrontend/src/pages/SurveyCreateAndEditingPage/SurveyCreateAndEditingPage.tsx index e2b19cf..a093d06 100644 --- a/SurveyFrontend/src/pages/SurveyEditingPage/SurveyEditingPage.tsx +++ b/SurveyFrontend/src/pages/SurveyCreateAndEditingPage/SurveyCreateAndEditingPage.tsx @@ -1,9 +1,9 @@ import Header from "../../components/Header/Header.tsx"; import Navigation from "../../components/Navigation/Navigation.tsx"; -import styles from './SurveyEditingPage.module.css' +import styles from './SurveyCreateAndEditingPage.module.css' import { Outlet } from "react-router-dom"; -export const SurveyEditingPage = () => { +export const SurveyCreateAndEditingPage = () => { return (