fix interaction with the survey
This commit is contained in:
parent
e961d53d6c
commit
15ec6b9632
9 changed files with 495 additions and 175 deletions
|
|
@ -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<number, number> = {};
|
||||
|
||||
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 <div>Загрузка...</div>;
|
||||
if (!survey) return <div>Опрос не найден</div>;
|
||||
|
||||
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 <div>Загрузка...</div>;
|
||||
if (!survey) return <div>Опрос не найден</div>;
|
||||
|
||||
return (
|
||||
<div className={styles.survey_page}>
|
||||
<SurveyInfo
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue