api requests
This commit is contained in:
parent
fe74490440
commit
5a1cc7c43c
22 changed files with 665 additions and 133 deletions
|
|
@ -1,18 +1,47 @@
|
|||
import React from 'react';
|
||||
import styles from './Account.module.css'
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styles from './Account.module.css';
|
||||
import AccountImg from '../../assets/account.svg?react';
|
||||
import { getCurrentUser } from '../../api/AuthApi';
|
||||
|
||||
interface AccountProps {
|
||||
href: string;
|
||||
user: string;
|
||||
}
|
||||
|
||||
const Account: React.FC<AccountProps> = ({href, user}) => {
|
||||
const Account: React.FC<AccountProps> = ({ href }) => {
|
||||
const [userName, setUserName] = useState<string>();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUserData = async () => {
|
||||
try {
|
||||
const userData = localStorage.getItem("user");
|
||||
|
||||
if (userData) {
|
||||
const parsedData = JSON.parse(userData);
|
||||
setUserName(`${parsedData.firstName} ${parsedData.lastName}`);
|
||||
} else {
|
||||
const data = await getCurrentUser();
|
||||
setUserName(`${data.firstName} ${data.lastName}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Ошибка загрузки данных пользователя:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchUserData();
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return <div className={styles.account}>Загрузка...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.account}>
|
||||
<a className={styles.accountText} href={href}>
|
||||
<AccountImg className={styles.accountImg}/>
|
||||
{user}
|
||||
{userName}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import React from "react";
|
||||
import Logo from "../Logo/Logo.tsx";
|
||||
import Account from "../Account/Account.tsx";
|
||||
import styles from './Header.module.css'
|
||||
import styles from './Header.module.css';
|
||||
import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||
|
||||
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
|
@ -22,19 +20,19 @@ const Header: React.FC = () => {
|
|||
<Logo href={location.pathname} onClick={handleLogoClick} />
|
||||
<nav className={styles.pagesNav}>
|
||||
<Link to='/survey/create/questions'
|
||||
className={`${styles.pageLink} ${isCreateSurveyActive ? styles.active : ''}`}>
|
||||
className={`${styles.pageLink} ${isCreateSurveyActive ? styles.active : ''}`}>
|
||||
Создать опрос
|
||||
{isCreateSurveyActive && <hr className={styles.activeLine}/>}
|
||||
</Link>
|
||||
<Link to='/my-surveys'
|
||||
className={`${styles.pageLink} ${isMySurveysPage ? styles.active : ''}`}>
|
||||
className={`${styles.pageLink} ${isMySurveysPage ? styles.active : ''}`}>
|
||||
Мои опросы
|
||||
{isMySurveysPage && <hr className={styles.activeLine}/>}
|
||||
</Link>
|
||||
</nav>
|
||||
<Account href={'/profile'} user={'Иванов Иван'}/>
|
||||
<Account href={'/profile'} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
export default Header;
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import styles from './MySurveysList.module.css'
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import {useEffect, useState} from "react";
|
||||
import {getMySurveys, ISurvey} from "../../api/SurveyApi.ts";
|
||||
import {deleteSurvey, getMySurveys, ISurvey} from "../../api/SurveyApi.ts";
|
||||
import Delete from '../../assets/delete.svg?react'
|
||||
|
||||
|
||||
interface MySurveyItem extends ISurvey{
|
||||
|
|
@ -35,34 +36,40 @@ export const MySurveyList = () => {
|
|||
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`)
|
||||
const handleSurveyClick = (surveyId: number) => {
|
||||
navigate(`/survey/${surveyId}/questions`)
|
||||
}
|
||||
|
||||
const handleDeleteClick = async (id: number, e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
const response = await deleteSurvey(id);
|
||||
|
||||
if (response?.success) {
|
||||
setSurveys(prev => prev.filter(survey => survey.id !== id));
|
||||
} else {
|
||||
console.error('Не удалось удалить опрос')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при удалении опроса:', error);
|
||||
|
||||
if (error instanceof Error && error.message.includes("401")) {
|
||||
navigate('/login');
|
||||
} else {
|
||||
alert("Ошибка при удалении опроса: " + (error instanceof Error ? error.message : 'Неизвестная ошибка'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return(
|
||||
<div className={styles.main}>
|
||||
{surveys.map((survey) => (
|
||||
<button
|
||||
<div
|
||||
key={survey.id}
|
||||
className={styles.survey}
|
||||
onClick={() => handleSurveyClick(survey.id)}
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
>
|
||||
<div className={styles.textContent}>
|
||||
<div className={styles.surveyData}>
|
||||
|
|
@ -71,12 +78,21 @@ export const MySurveyList = () => {
|
|||
</div>
|
||||
<span className={styles.date}>Дата создания: {survey.createdBy}</span>
|
||||
</div>
|
||||
<div className={`${styles.status} ${
|
||||
survey.status === 'active' ? styles.active : styles.completed
|
||||
}`}>
|
||||
{survey.status === 'active' ? 'Активен' : 'Завершён'}
|
||||
<div className={styles.container}>
|
||||
<div className={`${styles.status} ${
|
||||
survey.status === 'active' ? styles.active : styles.completed
|
||||
}`}>
|
||||
{survey.status === 'active' ? 'Активен' : 'Завершён'}
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => handleDeleteClick(survey.id, e)}
|
||||
className={styles.buttonDelete}
|
||||
>
|
||||
Удалить опрос {' '}
|
||||
<Delete className={styles.imgDelete}/>
|
||||
</button>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
.main {
|
||||
background-color: #F6F6F6;
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
min-height: 100vh;
|
||||
padding: 34px 10%;
|
||||
}
|
||||
|
|
@ -26,6 +27,32 @@
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
.container{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.buttonDelete{
|
||||
border-radius: 8px;
|
||||
align-items: center;
|
||||
background-color: #FFFFFF;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 5px 3px;
|
||||
color: black;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.buttonDelete:hover{
|
||||
background-color: #EDEDED;
|
||||
}
|
||||
|
||||
.imgDelete{
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.status {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React from 'react'
|
|||
import {useLocation, useNavigate} from 'react-router-dom'
|
||||
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();
|
||||
|
|
@ -41,7 +40,6 @@ const Navigation: React.FC = () => {
|
|||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
<SaveButton />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,16 +7,18 @@ import Delete from '../../assets/deleteQuestion.svg?react';
|
|||
|
||||
|
||||
interface QuestionItemProps {
|
||||
indexQuestion: number;
|
||||
questionId: number;
|
||||
initialTextQuestion?: string;
|
||||
valueQuestion: string;
|
||||
onChangeQuestion: (valueQuestion: string) => void;
|
||||
onDeleteQuestion: (index: number) => void;
|
||||
onDeleteQuestion: (index: number) => Promise<void>;
|
||||
selectedType: 'single' | 'multiply'; // Уточняем тип
|
||||
setSelectedType: (type: 'single' | 'multiply') => void; // Уточняем тип
|
||||
}
|
||||
|
||||
const QuestionItem: React.FC<QuestionItemProps> = ({indexQuestion, initialTextQuestion = `Вопрос ${indexQuestion}`,
|
||||
valueQuestion, onChangeQuestion, onDeleteQuestion}) => {
|
||||
const [selectedType, setSelectedType] = useState<'single' | 'multiply'>('single');
|
||||
const QuestionItem: React.FC<QuestionItemProps> = ({questionId, initialTextQuestion = `Вопрос ${questionId}`,
|
||||
valueQuestion, onChangeQuestion, onDeleteQuestion, setSelectedType, selectedType}) => {
|
||||
// const [selectedType, setSelectedType] = useState<'single' | 'multiply'>('single');
|
||||
const [answerOption, setAnswerOption] = useState(['']);
|
||||
const [textQuestion, setTextQuestion] = useState(initialTextQuestion);
|
||||
const [isEditingQuestion, setIsEditingQuestion] = useState(false);
|
||||
|
|
@ -86,8 +88,12 @@ const QuestionItem: React.FC<QuestionItemProps> = ({indexQuestion, initialTextQu
|
|||
setTextQuestion(valueQuestion);
|
||||
}, [valueQuestion]);
|
||||
|
||||
const handleDeleteQuestion = () => {
|
||||
onDeleteQuestion(indexQuestion);
|
||||
const handleDeleteQuestion = async () => {
|
||||
try {
|
||||
await onDeleteQuestion(questionId);
|
||||
} catch (error) {
|
||||
console.error('Ошибка при удалении вопроса:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSelect = (index: number) => {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,40 @@
|
|||
import React, { useState } from "react";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import QuestionItem from "../QuestionItem/QuestionItem.tsx";
|
||||
import AddQuestionButton from "../AddQuestionButton/AddQuestionButton.tsx";
|
||||
import {deleteQuestion} from "../../api/QuestionApi.ts";
|
||||
|
||||
interface QuestionsListProps {}
|
||||
|
||||
interface Question {
|
||||
id: number;
|
||||
text: string;
|
||||
interface QuestionsListProps {
|
||||
questions: Question[];
|
||||
setQuestions: (questions: Question[]) => void;
|
||||
surveyId?: number;
|
||||
}
|
||||
|
||||
const QuestionsList: React.FC<QuestionsListProps> = () => {
|
||||
const [questions, setQuestions] = useState<Question[]>([
|
||||
{ id: 1, text: '' },
|
||||
]);
|
||||
export interface Question {
|
||||
id: number;
|
||||
text: string;
|
||||
questionType: 'singleanswerquestion' | 'multipleanswerquestion';
|
||||
}
|
||||
|
||||
const QuestionsList: React.FC<QuestionsListProps> = ({questions, setQuestions, surveyId}) => {
|
||||
const [selectedType, setSelectedType] = useState<'single' | 'multiply'>('single');
|
||||
|
||||
const [localQuestionId, setLocalQuestionId] = useState(2); // Начинаем с 2, так как первый вопрос имеет ID=1
|
||||
|
||||
const handleAddQuestion = () => {
|
||||
const maxId = questions.reduce((max, question) => Math.max(max, question.id), 0);
|
||||
const newQuestion: Question = {
|
||||
id: maxId + 1,
|
||||
text: ''
|
||||
id: localQuestionId,
|
||||
text: '',
|
||||
questionType: selectedType === 'single' ? 'singleanswerquestion' : 'multipleanswerquestion',
|
||||
};
|
||||
setQuestions([...questions, newQuestion]);
|
||||
setLocalQuestionId(localQuestionId + 1);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLocalQuestionId(questions.length > 0 ?
|
||||
Math.max(...questions.map(q => q.id)) + 1 : 1);
|
||||
}, [questions]);
|
||||
|
||||
const handleQuestionChange = (id: number, value: string) => {
|
||||
const newQuestions = questions.map((question) =>
|
||||
question.id === id ? { ...question, text: value } : question
|
||||
|
|
@ -30,9 +42,25 @@ const QuestionsList: React.FC<QuestionsListProps> = () => {
|
|||
setQuestions(newQuestions);
|
||||
};
|
||||
|
||||
const handleDeleteQuestion = (id: number) => {
|
||||
const newQuestions = questions.filter((question) => question.id !== id);
|
||||
setQuestions(newQuestions);
|
||||
const handleDeleteQuestion = async (id: number) => {
|
||||
try {
|
||||
if (surveyId) {
|
||||
const response = await deleteQuestion(surveyId, id);
|
||||
if (!response?.success) {
|
||||
throw new Error('Не удалось удалить вопрос на сервере');
|
||||
}
|
||||
}
|
||||
const newQuestions: Question[] = [];
|
||||
for (const question of questions) {
|
||||
if (question.id !== id) {
|
||||
newQuestions.push(question);
|
||||
}
|
||||
}
|
||||
setQuestions(newQuestions);
|
||||
} catch (error) {
|
||||
console.error('Ошибка при удалении вопроса:', error);
|
||||
alert('Не удалось удалить вопрос: ' + (error instanceof Error ? error.message : 'Неизвестная ошибка'));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -40,10 +68,12 @@ const QuestionsList: React.FC<QuestionsListProps> = () => {
|
|||
{questions.map((question) => (
|
||||
<QuestionItem
|
||||
key={question.id}
|
||||
indexQuestion={question.id}
|
||||
questionId={question.id}
|
||||
valueQuestion={question.text}
|
||||
onDeleteQuestion={() => handleDeleteQuestion(question.id)}
|
||||
onChangeQuestion={(value) => handleQuestionChange(question.id, value)}
|
||||
selectedType={selectedType}
|
||||
setSelectedType={setSelectedType}
|
||||
/>
|
||||
))}
|
||||
<AddQuestionButton onClick={handleAddQuestion} />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,19 @@
|
|||
import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx";
|
||||
import styles from './Results.module.css'
|
||||
import {useState} from "react";
|
||||
|
||||
export const Results = () => {
|
||||
const [descriptionSurvey, setDescriptionSurvey] = useState('');
|
||||
const [titleSurvey, setTitleSurvey] = useState('Название опроса');
|
||||
|
||||
return(
|
||||
<div className={styles.results}>
|
||||
<SurveyInfo />
|
||||
<SurveyInfo
|
||||
titleSurvey={titleSurvey}
|
||||
descriptionSurvey={descriptionSurvey}
|
||||
setDescriptionSurvey={setDescriptionSurvey}
|
||||
setTitleSurvey={setTitleSurvey}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
/*SaveButton.module.css*/
|
||||
|
||||
.createSurveyButton {
|
||||
margin-left: 40px;
|
||||
display: block;
|
||||
margin: 10px auto;
|
||||
padding: 25px 50.5px;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
|
|
@ -12,4 +13,4 @@
|
|||
text-align: center;
|
||||
box-shadow: 0 0 7.4px 0 rgba(154, 202, 247, 1);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ import React from 'react'
|
|||
import styles from './SaveButton.module.css'
|
||||
|
||||
interface CreateSurveyButtonProps {
|
||||
// onClick(): void;
|
||||
onClick(): void;
|
||||
}
|
||||
|
||||
const SaveButton: React.FC<CreateSurveyButtonProps> = () => {
|
||||
const SaveButton: React.FC<CreateSurveyButtonProps> = ({onClick}) => {
|
||||
return (
|
||||
<button className={styles.createSurveyButton}>
|
||||
<button onClick={onClick} className={styles.createSurveyButton}>
|
||||
Сохранить
|
||||
</button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
import React from 'react';
|
||||
import React, {useState} from 'react';
|
||||
import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx";
|
||||
import styles from "./SettingSurvey.module.css";
|
||||
import TimeEvent from "../TimeEvent/TimeEvent.tsx";
|
||||
|
||||
|
||||
const SettingSurvey: React.FC = () => {
|
||||
const [descriptionSurvey, setDescriptionSurvey] = useState('');
|
||||
const [titleSurvey, setTitleSurvey] = useState('Название опроса');
|
||||
|
||||
return (
|
||||
<div className={styles.settingSurvey}>
|
||||
<SurveyInfo />
|
||||
<SurveyInfo
|
||||
titleSurvey={titleSurvey}
|
||||
descriptionSurvey={descriptionSurvey}
|
||||
setDescriptionSurvey={setDescriptionSurvey}
|
||||
setTitleSurvey={setTitleSurvey}
|
||||
/>
|
||||
<div className={styles.startEndTime}>
|
||||
<TimeEvent title='Время начала'/>
|
||||
<TimeEvent title='Время окончания'/>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,73 @@
|
|||
import React from "react";
|
||||
import React, {useState} from "react";
|
||||
import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx";
|
||||
import QuestionsList from "../QuestionsList/QuestionsList.tsx";
|
||||
import QuestionsList, {Question} from "../QuestionsList/QuestionsList.tsx";
|
||||
import styles from './Survey.module.css'
|
||||
import SaveButton from "../SaveButton/SaveButton.tsx";
|
||||
import {ISurvey, postNewSurvey} from "../../api/SurveyApi.ts";
|
||||
import {addNewQuestion} from "../../api/QuestionApi.ts";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
|
||||
const Survey: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const [descriptionSurvey, setDescriptionSurvey] = useState('');
|
||||
const [titleSurvey, setTitleSurvey] = useState('Название опроса');
|
||||
const [survey, setSurvey] = useState<ISurvey | null>(null);
|
||||
|
||||
const [questions, setQuestions] = useState<Question[]>([
|
||||
{ id: 1, text: '', questionType: 'singleanswerquestion'},
|
||||
]);
|
||||
|
||||
// const handleSave = async () => {
|
||||
// const savedSurvey = await postNewSurvey({title: titleSurvey, description: descriptionSurvey});
|
||||
// setSurvey(savedSurvey);
|
||||
// Promise.all(
|
||||
// questions
|
||||
// .map((question) => addNewQuestion( savedSurvey.id, {title: question.text, questionType: question.questionType })),
|
||||
// )
|
||||
// .then(() => {
|
||||
// alert('Все удачно сохранилось');
|
||||
// })
|
||||
// .catch(() => {
|
||||
// alert('Пиздец');
|
||||
// });
|
||||
// };
|
||||
|
||||
|
||||
const handleSave = async () => {
|
||||
const savedSurvey = await postNewSurvey({title: titleSurvey, description: descriptionSurvey});
|
||||
setSurvey(savedSurvey);
|
||||
|
||||
try {
|
||||
await Promise.all(
|
||||
questions.map(question =>
|
||||
addNewQuestion(savedSurvey.id, {title: question.text, questionType: question.questionType})
|
||||
))
|
||||
|
||||
// Сбрасываем состояние после сохранения
|
||||
setQuestions([{ id: 1, text: '', questionType: 'singleanswerquestion' }]);
|
||||
setTitleSurvey('Новый опрос');
|
||||
setDescriptionSurvey('');
|
||||
navigate('/my-surveys');
|
||||
} catch (error) {
|
||||
console.error('Ошибка при сохранении:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.survey}>
|
||||
<SurveyInfo />
|
||||
<QuestionsList />
|
||||
<SurveyInfo
|
||||
titleSurvey={titleSurvey}
|
||||
descriptionSurvey={descriptionSurvey}
|
||||
setDescriptionSurvey={setDescriptionSurvey}
|
||||
setTitleSurvey={setTitleSurvey}
|
||||
/>
|
||||
<QuestionsList
|
||||
questions={questions}
|
||||
setQuestions={setQuestions}
|
||||
surveyId={survey?.id}
|
||||
/>
|
||||
|
||||
<SaveButton onClick={handleSave}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,39 @@
|
|||
import React, {useState, useRef, useEffect} from "react";
|
||||
import styles from './SurveyInfo.module.css'
|
||||
import AddDescripImg from '../../assets/add_circle.svg?react';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
|
||||
const SurveyInfo: React.FC = () => {
|
||||
const [descriptionSurvey, setDescriptionSurvey] = useState('');
|
||||
const [titleSurvey, setTitleSurvey] = useState('Название опроса');
|
||||
|
||||
interface SurveyInfoProps {
|
||||
titleSurvey: string;
|
||||
descriptionSurvey: string;
|
||||
setDescriptionSurvey: (text: string) => void;
|
||||
setTitleSurvey: (text: string) => void;
|
||||
}
|
||||
|
||||
const SurveyInfo: React.FC<SurveyInfoProps> = ({titleSurvey, setDescriptionSurvey, descriptionSurvey, setTitleSurvey}) => {
|
||||
const [showDescriptionField, setShowDescriptionField] = useState(false);
|
||||
const [showNewTitleField, setShowNewTitleField] = useState(false);
|
||||
const titleTextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const descriptionTextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const adjustTextareaHeight = (textarea: HTMLTextAreaElement | null) => {
|
||||
if (textarea) {
|
||||
// Сброс высоты перед расчетом
|
||||
textarea.style.height = 'auto';
|
||||
// Устанавливаем высоту равной scrollHeight + небольшой отступ
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
// Центрируем содержимое вертикально
|
||||
textarea.style.paddingTop = `${Math.max(0, (textarea.clientHeight - textarea.scrollHeight) / 2)}px`;
|
||||
}
|
||||
};
|
||||
|
||||
const handleDescriptionChange = (descripEvent: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setDescriptionSurvey(descripEvent.target.value);
|
||||
adjustTextareaHeight(descripEvent.target);
|
||||
};
|
||||
|
||||
const handleNewTitleChange = (titleEvent: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setTitleSurvey(titleEvent.target.value);
|
||||
adjustTextareaHeight(titleEvent.target);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (showNewTitleField && titleTextareaRef.current) {
|
||||
titleTextareaRef.current.focus();
|
||||
adjustTextareaHeight(titleTextareaRef.current);
|
||||
}
|
||||
}, [showNewTitleField]);
|
||||
|
||||
useEffect(() => {
|
||||
if (showDescriptionField && descriptionTextareaRef.current) {
|
||||
descriptionTextareaRef.current.focus();
|
||||
adjustTextareaHeight(descriptionTextareaRef.current);
|
||||
}
|
||||
}, [showDescriptionField]);
|
||||
|
||||
|
|
@ -91,16 +83,16 @@ const SurveyInfo: React.FC = () => {
|
|||
} else if (showDescriptionField) {
|
||||
return (
|
||||
<div className={styles.descriptionWrapper}>
|
||||
<textarea
|
||||
ref={descriptionTextareaRef}
|
||||
className={styles.textareaDescrip}
|
||||
value={descriptionSurvey}
|
||||
placeholder={'Добавить описание'}
|
||||
onChange={handleDescriptionChange}
|
||||
onKeyDown={handleDescriptionKeyDown}
|
||||
onBlur={handleDescriptionBlur}
|
||||
rows={1} // Начальное количество строк
|
||||
/>
|
||||
<TextareaAutosize
|
||||
ref={descriptionTextareaRef}
|
||||
className={styles.textareaDescrip}
|
||||
value={descriptionSurvey}
|
||||
placeholder={'Добавить описание'}
|
||||
onChange={handleDescriptionChange}
|
||||
onKeyDown={handleDescriptionKeyDown}
|
||||
onBlur={handleDescriptionBlur}
|
||||
rows={1}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
|
|
@ -122,7 +114,7 @@ const SurveyInfo: React.FC = () => {
|
|||
{
|
||||
showNewTitleField ? (
|
||||
<h1 className={styles.titleSurvey}>
|
||||
<textarea className={styles.textareaTitle}
|
||||
<TextareaAutosize className={styles.textareaTitle}
|
||||
ref={titleTextareaRef}
|
||||
value={titleSurvey === 'Название опроса' ? '' : titleSurvey}
|
||||
placeholder={'Название опроса'}
|
||||
|
|
|
|||
61
SurveyFrontend/src/components/SurveyPage/SurveyPage.tsx
Normal file
61
SurveyFrontend/src/components/SurveyPage/SurveyPage.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import SurveyInfo from '../SurveyInfo/SurveyInfo.tsx';
|
||||
import QuestionsList, {Question} from '../QuestionsList/QuestionsList.tsx';
|
||||
import {getSurveyById, ISurvey} from "../../api/SurveyApi.ts";
|
||||
import {getListQuestions} from "../../api/QuestionApi.ts";
|
||||
|
||||
export const SurveyPage: React.FC = () => {
|
||||
// const navigate = useNavigate();
|
||||
const [survey, setSurvey] = useState<ISurvey | null>(null);
|
||||
const [questions, setQuestions] = useState<Question[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { id: surveyId } = useParams<{ id: string }>(); // Переименовываем для ясности
|
||||
console.log(surveyId);
|
||||
|
||||
useEffect(() => {
|
||||
if (!surveyId || isNaN(Number(surveyId))) {
|
||||
console.error('Invalid survey ID');
|
||||
return;
|
||||
}
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
// Получаем опрос по surveyId
|
||||
const surveyData = await getSurveyById(Number(surveyId));
|
||||
setSurvey(surveyData);
|
||||
|
||||
// Получаем вопросы опроса по surveyId
|
||||
const questionsData = await getListQuestions(Number(surveyId));
|
||||
const formattedQuestions = questionsData.map(q => ({
|
||||
id: q.id, // ID вопроса
|
||||
text: q.title,
|
||||
questionType: q.questionType,
|
||||
}));
|
||||
setQuestions(formattedQuestions as Question[]);
|
||||
} catch (error) {
|
||||
console.error('Ошибка:', error);
|
||||
// navigate('/my-surveys');
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, [surveyId]);
|
||||
|
||||
if (loading) return <div>Загрузка...</div>;
|
||||
if (!survey) return <div>Опрос не найден</div>;
|
||||
|
||||
return (
|
||||
<div className="survey-page">
|
||||
<SurveyInfo
|
||||
titleSurvey={survey.title}
|
||||
descriptionSurvey={survey.description}
|
||||
setDescriptionSurvey={() => {}}
|
||||
setTitleSurvey={() => {}}
|
||||
/>
|
||||
<QuestionsList
|
||||
questions={questions}
|
||||
setQuestions={() => {}}
|
||||
surveyId={survey.id}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue