From 15ec6b963237ece0faf7e7225458d7a0c5e951c8 Mon Sep 17 00:00:00 2001 From: Tatiana Nikolaeva Date: Sun, 25 May 2025 18:36:20 +0500 Subject: [PATCH] fix interaction with the survey --- SurveyFrontend/src/api/AnswerApi.ts | 25 +- SurveyFrontend/src/api/AuthApi.ts | 9 +- SurveyFrontend/src/api/QuestionApi.ts | 7 +- .../components/MySurveyList/MySurveyList.tsx | 22 +- .../components/QuestionItem/QuestionItem.tsx | 140 +++++--- .../QuestionsList/QuestionsList.tsx | 29 +- .../SettingSurvey/SettingSurvey.tsx | 32 +- .../src/components/Survey/Survey.tsx | 67 ++-- .../src/components/SurveyPage/SurveyPage.tsx | 339 ++++++++++++++---- 9 files changed, 495 insertions(+), 175 deletions(-) diff --git a/SurveyFrontend/src/api/AnswerApi.ts b/SurveyFrontend/src/api/AnswerApi.ts index 59ad95e..f29d5b4 100644 --- a/SurveyFrontend/src/api/AnswerApi.ts +++ b/SurveyFrontend/src/api/AnswerApi.ts @@ -1,5 +1,15 @@ import {BASE_URL, createRequestConfig, handleResponse} from "./BaseApi.ts"; +export interface INewAnswer{ + text: string; +} + +export interface IAnswerVariant extends INewAnswer{ + surveyId: number; + id: number; + questionId: number; +} + export const getAnswerVariants = async (surveyId: number, questionId: number) => { try{ const response = await fetch(`${BASE_URL}/surveys/${surveyId}/questions/${questionId}/answerVariants`, { @@ -12,7 +22,7 @@ export const getAnswerVariants = async (surveyId: number, questionId: number) => } } -export const addNewAnswerVariant = async (surveyId: number, questionId: number) => { +export const addNewAnswerVariant = async (surveyId: number, questionId: number, answer: INewAnswer) => { const token = localStorage.getItem("token"); if (!token) { throw new Error("Токен отсутствует"); @@ -22,8 +32,7 @@ export const addNewAnswerVariant = async (surveyId: number, questionId: number) const response = await fetch(`${BASE_URL}/surveys/${surveyId}/questions/${questionId}/answerVariants`, { ...createRequestConfig('POST'), body: JSON.stringify({ - surveyId: surveyId, - questionId: questionId, + text: answer.text, }), }) @@ -38,7 +47,7 @@ export const addNewAnswerVariant = async (surveyId: number, questionId: number) } } -export const updateAnswerVariant = async (surveyId: number, questionId: number, id: number) => { +export const updateAnswerVariant = async (surveyId: number, questionId: number, id: number, answer: INewAnswer): Promise => { const token = localStorage.getItem("token"); if (!token) { throw new Error("Токен отсутствует"); @@ -48,18 +57,18 @@ export const updateAnswerVariant = async (surveyId: number, questionId: number, const response = await fetch(`${BASE_URL}/surveys/${surveyId}/questions/${questionId}/answerVariants/${id}`, { ...createRequestConfig('PUT'), body: JSON.stringify({ - surveyId: surveyId, - questionId: questionId, - id: id + text: answer.text, }) }) if (!response.ok) { - throw new Error(`Ошибка: ${response.status}`); + const errorData = await response.json().catch(() => null); + throw new Error(`Ошибка ${response.status}: ${errorData?.message || 'Неизвестная ошибка'}`); } return await handleResponse(response) } catch(err){ console.error(`Error updating the response option: ${err}`); + throw err; } } diff --git a/SurveyFrontend/src/api/AuthApi.ts b/SurveyFrontend/src/api/AuthApi.ts index 26e2821..da61451 100644 --- a/SurveyFrontend/src/api/AuthApi.ts +++ b/SurveyFrontend/src/api/AuthApi.ts @@ -11,7 +11,7 @@ interface IRegistrationData extends IAuthData{ lastName: string; } -export const getCurrentUser = async (): Promise => { +export const getCurrentUser = async (user: IRegistrationData) => { const token = localStorage.getItem("token"); if (!token) { @@ -23,7 +23,12 @@ export const getCurrentUser = async (): Promise => { ...createRequestConfig('GET'), headers: { 'Authorization': `Bearer ${token}` - } + }, + body: JSON.stringify({ + email: user.email, + firstName: user.firstName, + lastName: user.lastName, + }) }); console.log(response); diff --git a/SurveyFrontend/src/api/QuestionApi.ts b/SurveyFrontend/src/api/QuestionApi.ts index d42fc17..90593b6 100644 --- a/SurveyFrontend/src/api/QuestionApi.ts +++ b/SurveyFrontend/src/api/QuestionApi.ts @@ -1,16 +1,11 @@ import {BASE_URL, createRequestConfig, handleResponse} from "./BaseApi.ts"; +import {IAnswerVariant} from "./AnswerApi.ts"; export interface INewQuestion{ title: string; questionType: string; } -export interface IAnswerVariant{ - id: number; - questionId: number; - text: string; -} - export interface IQuestion extends INewQuestion { id: number; surveyId: number; diff --git a/SurveyFrontend/src/components/MySurveyList/MySurveyList.tsx b/SurveyFrontend/src/components/MySurveyList/MySurveyList.tsx index 20c2f0d..963092a 100644 --- a/SurveyFrontend/src/components/MySurveyList/MySurveyList.tsx +++ b/SurveyFrontend/src/components/MySurveyList/MySurveyList.tsx @@ -25,12 +25,12 @@ export const MySurveyList = () => { } catch (error) { console.error('Ошибка при получении списка опросов:', error); - if (error instanceof Error && error.message.includes("401")) { - // Если ошибка 401, перенаправляем на страницу входа - navigate('/login'); - } else { - alert("Ошибка при загрузке опросов: " + (error instanceof Error && error.message)); - } + // if (error instanceof Error && error.message.includes("401")) { + // // Если ошибка 401, перенаправляем на страницу входа + // navigate('/login'); + // } else { + // alert("Ошибка при загрузке опросов: " + (error instanceof Error && error.message)); + // } } }; fetchSurvey(); @@ -53,11 +53,11 @@ export const MySurveyList = () => { } catch (error) { console.error('Ошибка при удалении опроса:', error); - if (error instanceof Error && error.message.includes("401")) { - navigate('/login'); - } else { - alert("Ошибка при удалении опроса: " + (error instanceof Error ? error.message : 'Неизвестная ошибка')); - } + // if (error instanceof Error && error.message.includes("401")) { + // navigate('/login'); + // } else { + // alert("Ошибка при удалении опроса: " + (error instanceof Error ? error.message : 'Неизвестная ошибка')); + // } } }; diff --git a/SurveyFrontend/src/components/QuestionItem/QuestionItem.tsx b/SurveyFrontend/src/components/QuestionItem/QuestionItem.tsx index 73c45b1..6ac4221 100644 --- a/SurveyFrontend/src/components/QuestionItem/QuestionItem.tsx +++ b/SurveyFrontend/src/components/QuestionItem/QuestionItem.tsx @@ -4,65 +4,97 @@ import AddAnswerButton from "../AddAnswerButton/AddAnswerButton.tsx"; import TypeDropdown from "../TypeDropdown/TypeDropdown.tsx"; import styles from './QuestionItem.module.css' import Delete from '../../assets/deleteQuestion.svg?react'; - +import { + addNewAnswerVariant, + deleteAnswerVariant, + getAnswerVariants, + updateAnswerVariant +} from "../../api/AnswerApi.ts"; interface QuestionItemProps { questionId: number; initialTextQuestion?: string; valueQuestion: string; + answerVariants: {id?: number, text: string}[]; onChangeQuestion: (valueQuestion: string) => void; + onAnswerVariantsChange: (variants: {id?: number, text: string}[]) => void; onDeleteQuestion: (index: number) => Promise; - selectedType: 'single' | 'multiply'; // Уточняем тип - setSelectedType: (type: 'single' | 'multiply') => void; // Уточняем тип + selectedType: 'single' | 'multiply'; + setSelectedType: (type: 'single' | 'multiply') => void; + surveyId?: number; } -const QuestionItem: React.FC = ({questionId, initialTextQuestion = `Вопрос ${questionId}`, - valueQuestion, onChangeQuestion, onDeleteQuestion, setSelectedType, selectedType}) => { - // const [selectedType, setSelectedType] = useState<'single' | 'multiply'>('single'); - const [answerOption, setAnswerOption] = useState(['']); +const QuestionItem: React.FC = ({ + questionId, + initialTextQuestion = `Вопрос ${questionId}`, + valueQuestion, + answerVariants: initialAnswerVariants, + onChangeQuestion, + onAnswerVariantsChange, + onDeleteQuestion, + setSelectedType, + selectedType, + surveyId + }) => { const [textQuestion, setTextQuestion] = useState(initialTextQuestion); const [isEditingQuestion, setIsEditingQuestion] = useState(false); - const [selectedAnswers, setSelectedAnswers] = useState([]); - const textareaQuestionRef = useRef(null); + useEffect(() => { + setTextQuestion(valueQuestion); + }, [valueQuestion]); + + // useEffect(() => { + // if (initialAnswerVariants.length === 0 && surveyId) { + // handleAddAnswer(); + // } + // }, [initialAnswerVariants.length, surveyId]); + const handleTypeChange = (type: 'single' | 'multiply') => { setSelectedType(type); - } + }; - const handleAddAnswer = () => { - setAnswerOption([...answerOption, '']); + const handleAddAnswer = async () => { + if (surveyId) { + try { + const newAnswer = await addNewAnswerVariant(surveyId, questionId, { text: '' }); + onAnswerVariantsChange([...initialAnswerVariants, { id: newAnswer.id, text: '' }]); + } catch (error) { + console.error('Ошибка при добавлении варианта ответа:', error); + } + } else { + onAnswerVariantsChange([...initialAnswerVariants, { text: '' }]); + } }; const handleQuestionClick = () => { setIsEditingQuestion(true); - } + }; const handleTextareaQuestionChange = (event: React.ChangeEvent) => { setTextQuestion(event.target.value); - if (textareaQuestionRef.current) { textareaQuestionRef.current.style.height = 'auto'; textareaQuestionRef.current.style.height = `${textareaQuestionRef.current.scrollHeight}px`; } - } + }; const handleSaveQuestion = () => { setIsEditingQuestion(false); onChangeQuestion(textQuestion); - } + }; const handleQuestionKeyDown = (keyDownEvent: React.KeyboardEvent) => { if (keyDownEvent.key === 'Enter') { keyDownEvent.preventDefault(); - handleSaveQuestion() + handleSaveQuestion(); } - } + }; const handleQuestionBlur = () => { - handleSaveQuestion() - } + handleSaveQuestion(); + }; useEffect(() => { if (isEditingQuestion && textareaQuestionRef.current) { @@ -72,21 +104,51 @@ const QuestionItem: React.FC = ({questionId, initialTextQuest } }, [isEditingQuestion]); - const handleAnswerChange = (index: number, value: string) => { - const newAnswerOption = [...answerOption]; - newAnswerOption[index] = value; - setAnswerOption(newAnswerOption); - } + const handleAnswerChange = async (index: number, value: string) => { + const newAnswerVariants = [...initialAnswerVariants]; + newAnswerVariants[index] = { ...newAnswerVariants[index], text: value }; + onAnswerVariantsChange(newAnswerVariants); - const handleDeleteAnswer = (index: number) => { - const newAnswerOption = answerOption.filter((_, i) => i !== index); - setAnswerOption(newAnswerOption); - setSelectedAnswers(selectedAnswers.filter((i) => i !== index)); + // Обновляем на сервере только если вариант уже существует (имеет id) + if (surveyId && newAnswerVariants[index].id) { + try { + await updateAnswerVariant( + surveyId, + questionId, + newAnswerVariants[index].id!, + { text: value } + ); + } catch (error) { + console.error('Ошибка при обновлении варианта ответа:', error); + } + } }; - useEffect(() => { - setTextQuestion(valueQuestion); - }, [valueQuestion]); + const handleDeleteAnswer = async (index: number) => { + const answerToDelete = initialAnswerVariants[index]; + + if (surveyId && answerToDelete.id) { + try { + await deleteAnswerVariant(surveyId, questionId, answerToDelete.id); + const newAnswerVariants = initialAnswerVariants.filter((_, i) => i !== index); + onAnswerVariantsChange(newAnswerVariants); + setSelectedAnswers(selectedAnswers.filter((i) => i !== index)); + + // Обновляем список после удаления + if (surveyId) { + const variants = await getAnswerVariants(surveyId, questionId); + const answers = variants.map((v: { id: number, text: string }) => ({ id: v.id, text: v.text })); + onAnswerVariantsChange(answers); + } + } catch (error) { + console.error('Ошибка при удалении варианта ответа:', error); + } + } else { + const newAnswerVariants = initialAnswerVariants.filter((_, i) => i !== index); + onAnswerVariantsChange(newAnswerVariants); + setSelectedAnswers(selectedAnswers.filter((i) => i !== index)); + } + }; const handleDeleteQuestion = async () => { try { @@ -133,12 +195,12 @@ const QuestionItem: React.FC = ({questionId, initialTextQuest - {answerOption.map((answerText, index) => ( + {initialAnswerVariants.map((answer, index) => ( handleAnswerChange(index, value)} onDelete={() => handleDeleteAnswer(index)} toggleSelect={() => toggleSelect(index)} @@ -146,17 +208,15 @@ const QuestionItem: React.FC = ({questionId, initialTextQuest ))}
- +
); -} +}; export default QuestionItem; \ No newline at end of file diff --git a/SurveyFrontend/src/components/QuestionsList/QuestionsList.tsx b/SurveyFrontend/src/components/QuestionsList/QuestionsList.tsx index 3162a33..8a4afb4 100644 --- a/SurveyFrontend/src/components/QuestionsList/QuestionsList.tsx +++ b/SurveyFrontend/src/components/QuestionsList/QuestionsList.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from "react"; +import React, {useState} from "react"; import QuestionItem from "../QuestionItem/QuestionItem.tsx"; import AddQuestionButton from "../AddQuestionButton/AddQuestionButton.tsx"; import {deleteQuestion, getListQuestions} from "../../api/QuestionApi.ts"; @@ -13,32 +13,25 @@ export interface Question { id: number; text: string; questionType: 'singleanswerquestion' | 'multipleanswerquestion'; - answerVariants?: { + answerVariants: Array<{ id?: number; text: string; - }[]; + }>; } const QuestionsList: React.FC = ({questions, setQuestions, surveyId}) => { const [selectedType, setSelectedType] = useState<'single' | 'multiply'>('single'); - const [localQuestionId, setLocalQuestionId] = useState(2); - const handleAddQuestion = () => { const newQuestion: Question = { - id: localQuestionId, // ID >= 1001 — новые вопросы + id: questions.length + 1, // ID >= 1001 — новые вопросы text: '', questionType: selectedType === 'single' ? 'singleanswerquestion' : 'multipleanswerquestion', + answerVariants: [{text: ''}], }; 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 @@ -74,6 +67,15 @@ const QuestionsList: React.FC = ({questions, setQuestions, s } }; + const handleAnswerVariantsChange = (questionId: number, newAnswerVariants: {id?: number, text: string}[]) => { + const newQuestions = questions.map(question => + question.id === questionId + ? {...question, answerVariants: newAnswerVariants} + : question + ); + setQuestions(newQuestions); + }; + return ( <> {questions.map((question) => ( @@ -81,10 +83,13 @@ const QuestionsList: React.FC = ({questions, setQuestions, s key={question.id} questionId={question.id} valueQuestion={question.text} + answerVariants={question.answerVariants} + onAnswerVariantsChange={(variants) => handleAnswerVariantsChange(question.id, variants)} onDeleteQuestion={() => handleDeleteQuestion(question.id)} onChangeQuestion={(value) => handleQuestionChange(question.id, value)} selectedType={selectedType} setSelectedType={setSelectedType} + surveyId={surveyId} /> ))} diff --git a/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.tsx b/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.tsx index 3e858f6..3a02b44 100644 --- a/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.tsx +++ b/SurveyFrontend/src/components/SettingSurvey/SettingSurvey.tsx @@ -2,11 +2,41 @@ import React, {useState} from 'react'; import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx"; import styles from "./SettingSurvey.module.css"; import TimeEvent from "../TimeEvent/TimeEvent.tsx"; +// import {useParams} from "react-router-dom"; +// import {getSurveyById, ISurvey} from "../../api/SurveyApi.ts"; const SettingSurvey: React.FC = () => { const [descriptionSurvey, setDescriptionSurvey] = useState(''); - const [titleSurvey, setTitleSurvey] = useState('Название опроса'); + // const [survey, setSurvey] = useState(null); + const [titleSurvey, setTitleSurvey] = useState(''); + // const { surveyId } = useParams<{ surveyId: string }>(); + + // useEffect(() => { + // if (!surveyId) { + // console.error('Survey ID is missing'); + // return; + // } + // const id = parseInt(surveyId); + // if (isNaN(id)) { + // console.error('Invalid survey ID'); + // return; + // } + // + // const fetchData = async () => { + // try { + // const surveyData = await getSurveyById(id); + // setSurvey(surveyData); + // setTitleSurvey(surveyData.title); + // setDescriptionSurvey(surveyData.description); + // } catch (error) { + // console.error('Ошибка:', error); + // } + // }; + // + // fetchData(); + // + // }, [surveyId]); return (
diff --git a/SurveyFrontend/src/components/Survey/Survey.tsx b/SurveyFrontend/src/components/Survey/Survey.tsx index 3874aa6..f9fe704 100644 --- a/SurveyFrontend/src/components/Survey/Survey.tsx +++ b/SurveyFrontend/src/components/Survey/Survey.tsx @@ -6,6 +6,7 @@ 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"; +import {addNewAnswerVariant} from "../../api/AnswerApi.ts"; const Survey: React.FC = () => { const navigate = useNavigate(); @@ -14,25 +15,9 @@ const Survey: React.FC = () => { const [survey] = useState(null); const [questions, setQuestions] = useState([ - { id: 1, text: '', questionType: 'singleanswerquestion'}, + { id: 1, text: '', questionType: 'singleanswerquestion', answerVariants: [{ text: '' }]}, ]); - // 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 () => { try { const savedSurvey = await postNewSurvey({ @@ -40,20 +25,48 @@ const Survey: React.FC = () => { description: descriptionSurvey }); - await Promise.all( - questions.map(question => - addNewQuestion(savedSurvey.id, { - title: question.text, - questionType: question.questionType - }) - ) - ); + // Сначала создаем все вопросы + const updatedQuestions: Question[] = []; + for (const question of questions) { + const newQuestion = await addNewQuestion(savedSurvey.id, { + title: question.text, + questionType: question.questionType + }); + // Создаем копию вопроса с новым ID + const updatedQuestion: Question = { + ...question, + id: newQuestion.id, + answerVariants: [] + }; + + // Затем создаем варианты ответов для каждого вопроса + if (question.answerVariants && question.answerVariants.length > 0) { + const newVariants = await Promise.all( + question.answerVariants.map(answer => + addNewAnswerVariant( + savedSurvey.id, + newQuestion.id, + { text: answer.text } + ) + ) + ); + + // Обновляем варианты ответов с их ID + updatedQuestion.answerVariants = newVariants.map((variant: { id: number, text: string }) => ({ + id: variant.id, + text: variant.text + })); + } + + updatedQuestions.push(updatedQuestion); + } + + // Обновляем состояние с новыми ID + setQuestions(updatedQuestions); navigate('/my-surveys'); - } catch (error) { console.error('Ошибка при сохранении:', error); - alert('Не удалось сохранить опрос'); } }; diff --git a/SurveyFrontend/src/components/SurveyPage/SurveyPage.tsx b/SurveyFrontend/src/components/SurveyPage/SurveyPage.tsx index bb7c74f..ce5a462 100644 --- a/SurveyFrontend/src/components/SurveyPage/SurveyPage.tsx +++ b/SurveyFrontend/src/components/SurveyPage/SurveyPage.tsx @@ -1,26 +1,174 @@ +import React, { useEffect, useState } from "react"; import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx"; -import QuestionsList, {Question} from "../QuestionsList/QuestionsList.tsx"; -import {useEffect, useState} from "react"; -import {getSurveyById, ISurvey, updateSurvey} from "../../api/SurveyApi.ts"; -import {useParams} from "react-router-dom"; -import {addNewQuestion, getListQuestions, updateQuestion} from "../../api/QuestionApi.ts"; +import QuestionsList, { Question } from "../QuestionsList/QuestionsList.tsx"; +import { getSurveyById, ISurvey, updateSurvey } from "../../api/SurveyApi.ts"; +import { useParams } from "react-router-dom"; +import { addNewQuestion, getListQuestions, updateQuestion, deleteQuestion } from "../../api/QuestionApi.ts"; import styles from "./SurveyPage.module.css"; import SaveButton from "../SaveButton/SaveButton.tsx"; +import { addNewAnswerVariant, deleteAnswerVariant, getAnswerVariants, IAnswerVariant, updateAnswerVariant } from "../../api/AnswerApi.ts"; -type ActionType = 'create' | 'update'; +// Типы для действий +type ActionType = + | 'update-survey' + | 'create-question' + | 'update-question' + | 'delete-question' + | 'create-answer' + | 'update-answer' + | 'delete-answer'; -class QuestionAction { +// Интерфейсы для данных действий +interface SurveyActionData { + id: number; + title: string; + description: string; +} + +interface QuestionActionData { + surveyId: number; + id?: number; + title: string; + questionType: 'singleanswerquestion' | 'multipleanswerquestion'; +} + +interface AnswerActionData { + surveyId: number; + questionId: number; + id?: number; + text: string; +} + +interface Action { type: ActionType; - data: { - surveyId: number; - id?: number; - title?: string; - questionType?: string; - }; + data: SurveyActionData | QuestionActionData | AnswerActionData; + tempId?: number; +} - constructor(type: ActionType, data: { surveyId: number, id?: number, title?: string, questionType?: string }) { - this.type = type; - this.data = data; +class ActionQueue { + private actions: Action[] = []; + private idMap: Record = {}; + + add(action: Action) { + this.actions.push(action); + } + + async execute() { + console.log(`Выполнение очереди с ${this.actions.length} действиями`); + for (const [index, action] of this.actions.entries()) { + console.log(`Обработка действия ${index + 1}/${this.actions.length}:`, action); + try { + // ... существующий код + } catch (error) { + console.error(`Ошибка в действии ${index + 1}:`, error); + throw error; + } + } + for (const action of this.actions) { + try { + switch (action.type) { + case 'update-survey': + await this.handleUpdateSurvey(action.data as SurveyActionData); + break; + case 'create-question': { + const createdQuestion = await this.handleCreateQuestion(action.data as QuestionActionData); + if (action.tempId) { + this.idMap[action.tempId] = createdQuestion.id; + } + break; + } + case 'update-question': + await this.handleUpdateQuestion(action.data as QuestionActionData & { id: number }); + break; + case 'delete-question': + await this.handleDeleteQuestion(action.data as QuestionActionData & { id: number }); + break; + case 'create-answer': { + const answerData = action.data as AnswerActionData; + await this.handleCreateAnswer({ + ...answerData, + questionId: this.idMap[answerData.questionId] || answerData.questionId + }); + break; + } + case 'update-answer': { + const updateAnswerData = action.data as AnswerActionData & { id: number }; + await this.handleUpdateAnswer({ + ...updateAnswerData, + questionId: this.idMap[updateAnswerData.questionId] || updateAnswerData.questionId + }); + break; + } + case 'delete-answer': { + const deleteAnswerData = action.data as AnswerActionData & { id: number }; + await this.handleDeleteAnswer({ + ...deleteAnswerData, + questionId: this.idMap[deleteAnswerData.questionId] || deleteAnswerData.questionId + }); + break; + } + } + } catch (error) { + console.error(`Failed to execute action ${action.type}:`, error); + throw error; + } + } + } + + private async handleUpdateSurvey(data: SurveyActionData) { + return await updateSurvey(data.id, { + title: data.title, + description: data.description + }); + } + + private async handleCreateQuestion(data: QuestionActionData) { + return await addNewQuestion(data.surveyId, { + title: data.title, + questionType: data.questionType + }); + } + + private async handleUpdateQuestion(data: QuestionActionData & { id: number }) { + return await updateQuestion(data.surveyId, data.id, { + title: data.title, + questionType: data.questionType + }); + } + + private async handleDeleteQuestion(data: QuestionActionData & { id: number }) { + return await deleteQuestion(data.surveyId, data.id); + } + + private async handleCreateAnswer(data: AnswerActionData) { + return await addNewAnswerVariant(data.surveyId, data.questionId, { + text: data.text + }); + } + + private async handleUpdateAnswer(data: AnswerActionData & { id: number }) { + console.log('1. Начало handleUpdateAnswer', data); + try { + console.log('2. Перед вызовом updateAnswerVariant', { + surveyId: data.surveyId, + questionId: data.questionId, + id: data.id, + text: data.text + }); + const result = await updateAnswerVariant(data.surveyId, data.questionId, data.id, { + text: data.text + }); + console.log('3. После вызова updateAnswerVariant', result); + return result; + } catch (error) { + console.error('4. Ошибка в handleUpdateAnswer:', error); + throw error; + } + + } + + private async handleDeleteAnswer(data: AnswerActionData & { id: number }) { + return await deleteAnswerVariant(data.surveyId, data.questionId, data.id); } } @@ -34,7 +182,6 @@ export const SurveyPage: React.FC = () => { const [description, setDescription] = useState(''); const [title, setTitle] = useState(''); - useEffect(() => { if (!surveyId) { console.error('Survey ID is missing'); @@ -52,20 +199,26 @@ export const SurveyPage: React.FC = () => { setLoading(true); const surveyData = await getSurveyById(id); setSurvey(surveyData); - setTitle(surveyData.title); setDescription(surveyData.description); const questionsData = await getListQuestions(id); - const formattedQuestions = questionsData.map(q => ({ - id: q.id, - text: q.title, - questionType: q.questionType as 'singleanswerquestion' | 'multipleanswerquestion', + const formattedQuestions = await Promise.all(questionsData.map(async q => { + const answerVariants = await getAnswerVariants(id, 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('Не удалось загрузить опрос') + setError('Не удалось загрузить опрос'); } finally { setLoading(false); } @@ -74,77 +227,127 @@ export const SurveyPage: React.FC = () => { fetchData(); }, [surveyId]); - if (loading) return
Загрузка...
; - if (!survey) return
Опрос не найден
; - const handleSave = async () => { - if (!surveyId || !survey) return; + console.log('0. Начало handleSave'); + if (!surveyId || !survey) { + console.log('0.1. Прерывание - нет surveyId или survey', { surveyId, survey }); + return; + } try { setError(null); const id = parseInt(surveyId); + const actionQueue = new ActionQueue(); - const surveyUpdated = await updateSurvey(id, { - title: title, - description: description, + // 1. Обновление опроса + actionQueue.add({ + type: 'update-survey', + data: { id, title, description } as SurveyActionData }); - setSurvey(surveyUpdated); - const actions: QuestionAction[] = []; + // 2. Получаем текущие вопросы с сервера const serverQuestions = await getListQuestions(id); + // 3. Обработка вопросов questions.forEach(question => { - const existsOnServer = serverQuestions.some(q => q.id === question.id); + const isNewQuestion = !serverQuestions.some(q => q.id === question.id); - if (existsOnServer) { - actions.push(new QuestionAction("update", { - surveyId: id, - id: question.id, - title: question.text, - questionType: question.questionType - })); + if (isNewQuestion) { + actionQueue.add({ + type: 'create-question', + data: { + surveyId: id, + title: question.text, + questionType: question.questionType + } as QuestionActionData, + tempId: question.id + }); } else { - actions.push(new QuestionAction("create", { - surveyId: id, - title: question.text, - questionType: question.questionType - })); + // Обновляем только если текст изменился + const serverQuestion = serverQuestions.find(q => q.id === question.id); + if (serverQuestion && (serverQuestion.title !== question.text || serverQuestion.questionType !== question.questionType)) { + actionQueue.add({ + type: 'update-question', + data: { + surveyId: id, + id: question.id, + title: question.text, + questionType: question.questionType + } as QuestionActionData & { id: number } + }); + } + } + + // 4. Обработка вариантов ответов + if (question.answerVariants) { + question.answerVariants.forEach(answer => { + if (isNewQuestion || !answer.id) { + actionQueue.add({ + type: 'create-answer', + data: { + surveyId: id, + questionId: question.id, + text: answer.text + } as AnswerActionData, + tempId: question.id + }); + } else { + // Обновляем только если текст изменился + actionQueue.add({ + type: 'update-answer', + data: { + surveyId: id, + questionId: question.id, + id: answer.id, + text: answer.text + } as AnswerActionData & { id: number } + }); + } + }); } }); - for (const action of actions) { - switch (action.type) { - case "create": - await addNewQuestion(id, { - title: action.data.title as string, - questionType: action.data.questionType as 'singleanswerquestion' | 'multipleanswerquestion', - }); - break; - case "update": - if (action.data.id) { - await updateQuestion(id, action.data.id, { - title: action.data.title, - questionType: action.data.questionType - }); - } - break; + // 5. Удаление удаленных вопросов + serverQuestions.forEach(serverQuestion => { + if (!questions.some(q => q.id === serverQuestion.id)) { + actionQueue.add({ + type: 'delete-question', + data: { + surveyId: id, + id: serverQuestion.id + } as QuestionActionData & { id: number } + }); } - } + }); + // Выполняем все действия + await actionQueue.execute(); + + // Обновляем данные const updatedQuestions = await getListQuestions(id); - const formattedQuestions = updatedQuestions.map(q => ({ - id: q.id, - text: q.title, - questionType: q.questionType as 'singleanswerquestion' | 'multipleanswerquestion', + const formattedQuestions = await Promise.all(updatedQuestions.map(async q => { + const answerVariants = await getAnswerVariants(id, 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('Не удалось сохранить изменения'); + console.error('Ошибка сохранения:', error); + setError(`Ошибка сохранения: ${error instanceof Error ? error.message : 'Неизвестная ошибка'}`); } }; + if (loading) return
Загрузка...
; + if (!survey) return
Опрос не найден
; + return (