diff --git a/SurveyFrontend/src/App.tsx b/SurveyFrontend/src/App.tsx index 3f02788..7fa7945 100644 --- a/SurveyFrontend/src/App.tsx +++ b/SurveyFrontend/src/App.tsx @@ -9,7 +9,6 @@ import {MySurveyList} from "./components/MySurveyList/MySurveyList.tsx"; import AuthForm from "./pages/AuthForm/AuthForm.tsx"; import {SurveyPage} from "./components/SurveyPage/SurveyPage.tsx"; import CompleteSurvey from "./pages/CompleteSurvey/CompleteSurvey.tsx"; -import CompletingSurvey from "./components/CompletingSurvey/CompletingSurvey.tsx"; const App = () => { return( @@ -33,9 +32,7 @@ const App = () => { } /> - }> - }/> - + }/> } /> diff --git a/SurveyFrontend/src/components/AnswerOption/AnswerOption.module.css b/SurveyFrontend/src/components/AnswerOption/AnswerOption.module.css index 9f0ce82..ac05b53 100644 --- a/SurveyFrontend/src/components/AnswerOption/AnswerOption.module.css +++ b/SurveyFrontend/src/components/AnswerOption/AnswerOption.module.css @@ -15,8 +15,9 @@ font-size: 18px; font-weight: 500; word-break: break-word; - width: 70%; + /*width: 70%;*/ padding: 0; + margin-right: 150px; line-height: 24px; cursor: text; margin-top: 2px; diff --git a/SurveyFrontend/src/components/AnswerOption/AnswerOption.tsx b/SurveyFrontend/src/components/AnswerOption/AnswerOption.tsx index 1bf5bb5..0edebf4 100644 --- a/SurveyFrontend/src/components/AnswerOption/AnswerOption.tsx +++ b/SurveyFrontend/src/components/AnswerOption/AnswerOption.tsx @@ -6,6 +6,7 @@ import Multiple from '../../assets/emptyCheckbox.svg?react'; import SelectedSingle from '../../assets/radio_button_checked.svg?react' import SelectedMultiple from '../../assets/check_box.svg?react'; import TextareaAutosize from 'react-textarea-autosize'; +import {useRouteReadOnly} from "../../hooks/useRouteReadOnly.ts"; interface AnswerOptionProps{ index: number; @@ -15,14 +16,14 @@ interface AnswerOptionProps{ selectedType: 'SingleAnswerQuestion' | 'MultipleAnswerQuestion'; isSelected?: boolean; toggleSelect?: () => void; - isCompleteSurveyActive?: boolean; } -const AnswerOption: React.FC = ({index, value, onChange, onDelete, selectedType, isSelected, toggleSelect, isCompleteSurveyActive = false}) => { +const AnswerOption: React.FC = ({index, value, onChange, onDelete, selectedType, isSelected, toggleSelect}) => { const [currentValue, setCurrentValue] = useState(value); const [isEditing, setIsEditing] = useState(false); const textAreaRef = useRef(null); + const isReadOnly = useRouteReadOnly(); useEffect(() => { setCurrentValue(value); @@ -71,7 +72,7 @@ const AnswerOption: React.FC = ({index, value, onChange, onDe }, [isEditing]); const handleMarkerClick = () => { - if (isCompleteSurveyActive && toggleSelect) { + if (isReadOnly && toggleSelect) { toggleSelect(); } }; @@ -79,7 +80,7 @@ const AnswerOption: React.FC = ({index, value, onChange, onDe return (
- {isCompleteSurveyActive ? ( + {isReadOnly ? ( )} - {isCompleteSurveyActive ? ( + {isReadOnly ? ( @@ -128,7 +129,7 @@ const AnswerOption: React.FC = ({index, value, onChange, onDe )} - {!isCompleteSurveyActive && ( + {!isReadOnly && ( diff --git a/SurveyFrontend/src/components/CompletingSurvey/CompletingSurvey.tsx b/SurveyFrontend/src/components/CompletingSurvey/CompletingSurvey.tsx index 69eb87f..0bf3150 100644 --- a/SurveyFrontend/src/components/CompletingSurvey/CompletingSurvey.tsx +++ b/SurveyFrontend/src/components/CompletingSurvey/CompletingSurvey.tsx @@ -1,25 +1,85 @@ import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx"; import QuestionsList, {Question} from "../QuestionsList/QuestionsList.tsx"; -import {useState} from "react"; +import {useEffect, useState} from "react"; import styles from './CompletingSurvey.module.css' +import { useParams} from "react-router-dom"; +import {getSurveyById, ISurvey} from "../../api/SurveyApi.ts"; +import {getListQuestions} from "../../api/QuestionApi.ts"; +import {getAnswerVariants, IAnswerVariant} from "../../api/AnswerVariantsApi.ts"; export const CompletingSurvey = () => { - const [titleSurvey, setTitleSurvey] = useState("Название опроса"); - const [descriptionSurvey, setDescriptionSurvey] = useState(""); - const [questions, setQuestions] = useState([ - { id: 1, text: 'Вопрос 1', questionType: 'SingleAnswerQuestion', answerVariants: [{ id: 1, text: 'Ответ 1' }, - { id: 2, text: 'Ответ 1' }, { id: 3, text: 'Ответ 1' }]}, - { id: 2, text: 'Вопрос 2', questionType: 'MultipleAnswerQuestion', answerVariants: [{ id: 1, text: 'Ответ 1' }, - { id: 2, text: 'Ответ 1' }, { id: 3, text: 'Ответ 1' }]} - ]); + // const [titleSurvey, setTitleSurvey] = useState("Название опроса"); + // const [descriptionSurvey, setDescriptionSurvey] = useState(""); + // const [questions, setQuestions] = useState([ + // { id: 1, text: 'Вопрос 1', questionType: 'SingleAnswerQuestion', answerVariants: [{ id: 1, text: 'Ответ 1' }, + // { id: 2, text: 'Ответ 1' }, { id: 3, text: 'Ответ 1' }]}, + // { id: 2, text: 'Вопрос 2', questionType: 'MultipleAnswerQuestion', answerVariants: [{ id: 1, text: 'Ответ 1' }, + // { id: 2, text: 'Ответ 1' }, { id: 3, text: 'Ответ 1' }]} + // ]); + + // const [questions, setQuestions] = useState([]); + // const [loading, setLoading] = useState(true); + // const [error, setError] = useState(null); + // + // const [description, setDescription] = useState(''); + // const [title, setTitle] = useState(''); + // + // const { survey, setSurvey } = useOutletContext<{ + // survey: ISurvey; + // setSurvey: (survey: ISurvey) => void; + // }>(); + + const {surveyId} = useParams<{surveyId: string}>(); + const [survey, setSurvey] = useState(null); + const [questions, setQuestions] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchSurveyData = async () => { + try { + setLoading(true); + if (!surveyId) return; + + const surveyData = await getSurveyById(parseInt(surveyId)); + setSurvey(surveyData); + + const questionsData = await getListQuestions(parseInt(surveyId)); + const formattedQuestions = await Promise.all(questionsData.map(async q => { + const answerVariants = await getAnswerVariants(parseInt(surveyId), q.id); + return { + id: q.id, + text: q.title, + questionType: q.questionType as 'SingleAnswerQuestion' | 'MultipleAnswerQuestion', + answerVariants: answerVariants.map((a: IAnswerVariant) => ({ + id: a.id, + text: a.text + })) + }; + })); + setQuestions(formattedQuestions); + } catch (error) { + console.error('Ошибка загрузки опроса:', error); + setError('Не удалось загрузить опрос'); + } finally { + setLoading(false); + } + }; + + fetchSurveyData(); + }, [surveyId]); + + if (loading) return
Загрузка...
; + if (error) return
{error}
; + if (!survey) return
Опрос не найден
; return (
setSurvey({ ...survey, description: value })} + setTitleSurvey={(value) => setSurvey({ ...survey, title: value })} /> { const isCreateSurveyActive = location.pathname.startsWith('/survey/create'); const isMySurveysActive = location.pathname === '/my-surveys'; - const isCompleteSurveyActive = location.pathname === '/complete-survey'; const isSurveyViewPage = location.pathname.startsWith('/survey/') && !location.pathname.startsWith('/survey/create'); @@ -37,13 +36,6 @@ const Header: React.FC = () => { Мои опросы {(isMySurveysActive || isSurveyViewPage) &&
} - - Прохождение опроса - {isCompleteSurveyActive &&
} -
diff --git a/SurveyFrontend/src/components/QuestionItem/QuestionItem.tsx b/SurveyFrontend/src/components/QuestionItem/QuestionItem.tsx index fc1cd8b..4b2b458 100644 --- a/SurveyFrontend/src/components/QuestionItem/QuestionItem.tsx +++ b/SurveyFrontend/src/components/QuestionItem/QuestionItem.tsx @@ -10,8 +10,8 @@ import { getAnswerVariants, updateAnswerVariant } from "../../api/AnswerVariantsApi.ts"; -import {useLocation} from "react-router-dom"; import TextareaAutosize from "react-textarea-autosize"; +import {useRouteReadOnly} from "../../hooks/useRouteReadOnly.ts"; interface QuestionItemProps { questionId: number; @@ -23,7 +23,6 @@ interface QuestionItemProps { onDeleteQuestion: (index: number) => Promise; initialQuestionType: 'SingleAnswerQuestion' | 'MultipleAnswerQuestion'; onQuestionTypeChange: (type: 'SingleAnswerQuestion' | 'MultipleAnswerQuestion') => void; - surveyId?: number; } @@ -45,8 +44,7 @@ const QuestionItem: React.FC = ({ const [questionType, setQuestionType] = useState<'SingleAnswerQuestion' | 'MultipleAnswerQuestion'>(initialQuestionType); const textareaQuestionRef = useRef(null); - const location = useLocation(); - const isCompleteSurveyActive = location.pathname === '/complete-survey'; + const isReadOnly = useRouteReadOnly(); useEffect(() => { @@ -63,6 +61,8 @@ const QuestionItem: React.FC = ({ }; const handleAddAnswer = async () => { + if (isReadOnly) return + if (!surveyId) { onAnswerVariantsChange([...initialAnswerVariants, { text: '' }]); return; @@ -167,10 +167,8 @@ const QuestionItem: React.FC = ({ const toggleSelect = (index: number) => { if (initialQuestionType === 'SingleAnswerQuestion') { - // Для одиночного выбора: заменяем массив одним выбранным индексом setSelectedAnswers([index]); } else { - // Для множественного выбора: добавляем/удаляем индекс setSelectedAnswers(prev => prev.includes(index) ? prev.filter(i => i !== index) @@ -181,7 +179,7 @@ const QuestionItem: React.FC = ({ return (
- {isCompleteSurveyActive ? ( + {isReadOnly ? (

{textQuestion || initialTextQuestion}

@@ -194,7 +192,6 @@ const QuestionItem: React.FC = ({ value={answer.text} isSelected={selectedAnswers.includes(index)} toggleSelect={() => toggleSelect(index)} - isCompleteSurveyActive={isCompleteSurveyActive} /> ))}
diff --git a/SurveyFrontend/src/components/QuestionsList/QuestionsList.tsx b/SurveyFrontend/src/components/QuestionsList/QuestionsList.tsx index fa40be2..24c6dc4 100644 --- a/SurveyFrontend/src/components/QuestionsList/QuestionsList.tsx +++ b/SurveyFrontend/src/components/QuestionsList/QuestionsList.tsx @@ -3,8 +3,8 @@ import QuestionItem from "../QuestionItem/QuestionItem.tsx"; import AddQuestionButton from "../AddQuestionButton/AddQuestionButton.tsx"; import {addNewQuestion, deleteQuestion, getListQuestions} from "../../api/QuestionApi.ts"; import {addNewAnswerVariant} from "../../api/AnswerVariantsApi.ts"; -import {useLocation} from "react-router-dom"; import styles from './QuestionsList.module.css' +import {useRouteReadOnly} from "../../hooks/useRouteReadOnly.ts"; interface QuestionsListProps { questions: Question[]; @@ -23,8 +23,7 @@ export interface Question { } const QuestionsList: React.FC = ({questions, setQuestions, surveyId}) => { - const location = useLocation(); - const isCompleteSurveyActive = location.pathname === '/complete-survey'; + const isReadOnly = useRouteReadOnly(); const handleAddQuestion = async () => { if (!surveyId) { @@ -128,7 +127,7 @@ const QuestionsList: React.FC = ({questions, setQuestions, s surveyId={surveyId} /> ))} - {!isCompleteSurveyActive ? : ( + {!isReadOnly ? : ( )} diff --git a/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.module.css b/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.module.css index 444399f..59134d9 100644 --- a/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.module.css +++ b/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.module.css @@ -24,4 +24,19 @@ font-size: 24px; font-weight: 600; border-radius: 4px; +} + +.copyButton { + padding: 10px 15px; + background-color: #4CAF50; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 16px; + margin-bottom: 20px; +} + +.copyButton:hover { + background-color: #45a049; } \ No newline at end of file diff --git a/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.tsx b/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.tsx index a2a6568..f64f8ec 100644 --- a/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.tsx +++ b/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.tsx @@ -18,6 +18,15 @@ const SettingSurvey: React.FC = () => { const [descriptionSurvey, setDescriptionSurvey] = useState(''); const [titleSurvey, setTitleSurvey] = useState(''); + const handleCopyLink = () => { + if (!survey?.id) + return; + const link = `${window.location.origin}/complete-survey/${survey.id}`; + navigator.clipboard.writeText(link) + .then(() => console.log('Copied!')) + .catch(error => console.error(`Не удалось скопировать ссылку: ${error}`)); + } + return (
{isSettingCreatePage ? ( @@ -43,6 +52,7 @@ const SettingSurvey: React.FC = () => {

Параметры видимости

{}}/> +
) } diff --git a/SurveyFrontend/src/components/SurveyInfo/SurveyInfo.tsx b/SurveyFrontend/src/components/SurveyInfo/SurveyInfo.tsx index 3ce15ff..41e015a 100644 --- a/SurveyFrontend/src/components/SurveyInfo/SurveyInfo.tsx +++ b/SurveyFrontend/src/components/SurveyInfo/SurveyInfo.tsx @@ -3,6 +3,7 @@ import styles from './SurveyInfo.module.css' import AddDescripImg from '../../assets/add_circle.svg?react'; import TextareaAutosize from 'react-textarea-autosize'; import {useLocation} from "react-router-dom"; +import {useRouteReadOnly} from "../../hooks/useRouteReadOnly.ts"; interface SurveyInfoProps { @@ -20,10 +21,11 @@ const SurveyInfo: React.FC = ({titleSurvey, setDescriptionSurve const descriptionTextareaRef = useRef(null); const location = useLocation(); - const isCompleteSurveyActive = location.pathname === '/complete-survey'; const isSurveyViewPage = location.pathname.startsWith('/survey/') && !location.pathname.startsWith('/survey/create'); + const isReadOnly = useRouteReadOnly(); + const handleDescriptionChange = (descripEvent: React.ChangeEvent) => { setDescriptionSurvey?.(descripEvent.target.value); }; @@ -88,7 +90,7 @@ const SurveyInfo: React.FC = ({titleSurvey, setDescriptionSurve } const renderTitle = () => { - if (isCompleteSurveyActive) { + if (isReadOnly) { return (
diff --git a/SurveyFrontend/src/components/SurveyPage/SurveyPage.tsx b/SurveyFrontend/src/components/SurveyPage/SurveyPage.tsx index 8f70177..6b5dfbc 100644 --- a/SurveyFrontend/src/components/SurveyPage/SurveyPage.tsx +++ b/SurveyFrontend/src/components/SurveyPage/SurveyPage.tsx @@ -153,11 +153,9 @@ class ActionQueue { } export const SurveyPage: React.FC = () => { - // const [survey, setSurvey] = useState(null); const [questions, setQuestions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - // const { surveyId } = useParams<{ surveyId: string }>(); const [description, setDescription] = useState(''); const [title, setTitle] = useState(''); @@ -173,7 +171,6 @@ export const SurveyPage: React.FC = () => { return; } - // const id = parseInt(survey.id); const id = survey.id; if (isNaN(id)) { console.error('Invalid survey ID'); @@ -183,7 +180,6 @@ export const SurveyPage: React.FC = () => { const fetchData = async () => { try { setLoading(true); - // const surveyData = await getSurveyById(id); setSurvey(survey); setTitle(survey.title); setDescription(survey.description); @@ -218,7 +214,6 @@ export const SurveyPage: React.FC = () => { try { setError(null); - // const id = parseInt(survey.id); const id = survey.id; const actionQueue = new ActionQueue(); diff --git a/SurveyFrontend/src/hooks/useRouteReadOnly.ts b/SurveyFrontend/src/hooks/useRouteReadOnly.ts new file mode 100644 index 0000000..417a8c3 --- /dev/null +++ b/SurveyFrontend/src/hooks/useRouteReadOnly.ts @@ -0,0 +1,6 @@ +import { useLocation } from 'react-router-dom'; + +export const useRouteReadOnly = () => { + const location = useLocation(); + return location.pathname.includes('/complete-survey/'); +}; \ No newline at end of file diff --git a/SurveyFrontend/src/pages/CompleteSurvey/CompleteSurvey.tsx b/SurveyFrontend/src/pages/CompleteSurvey/CompleteSurvey.tsx index 32d9cd8..eed8f24 100644 --- a/SurveyFrontend/src/pages/CompleteSurvey/CompleteSurvey.tsx +++ b/SurveyFrontend/src/pages/CompleteSurvey/CompleteSurvey.tsx @@ -1,11 +1,9 @@ -import Header from "../../components/Header/Header.tsx"; import styles from './CompleteSurvey.module.css' import CompletingSurvey from "../../components/CompletingSurvey/CompletingSurvey.tsx"; export const CompleteSurvey = () => { return(
-
)