222 lines
No EOL
9.1 KiB
TypeScript
222 lines
No EOL
9.1 KiB
TypeScript
import React, {useState, useRef, useEffect} from "react";
|
||
import AnswerOption from '../AnswerOption/AnswerOption';
|
||
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<void>;
|
||
selectedType: 'single' | 'multiply';
|
||
setSelectedType: (type: 'single' | 'multiply') => void;
|
||
surveyId?: number;
|
||
}
|
||
|
||
const QuestionItem: React.FC<QuestionItemProps> = ({
|
||
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<number[]>([]);
|
||
const textareaQuestionRef = useRef<HTMLTextAreaElement>(null);
|
||
|
||
useEffect(() => {
|
||
setTextQuestion(valueQuestion);
|
||
}, [valueQuestion]);
|
||
|
||
// useEffect(() => {
|
||
// if (initialAnswerVariants.length === 0 && surveyId) {
|
||
// handleAddAnswer();
|
||
// }
|
||
// }, [initialAnswerVariants.length, surveyId]);
|
||
|
||
const handleTypeChange = (type: 'single' | 'multiply') => {
|
||
setSelectedType(type);
|
||
};
|
||
|
||
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<HTMLTextAreaElement>) => {
|
||
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<HTMLTextAreaElement>) => {
|
||
if (keyDownEvent.key === 'Enter') {
|
||
keyDownEvent.preventDefault();
|
||
handleSaveQuestion();
|
||
}
|
||
};
|
||
|
||
const handleQuestionBlur = () => {
|
||
handleSaveQuestion();
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (isEditingQuestion && textareaQuestionRef.current) {
|
||
textareaQuestionRef.current.focus();
|
||
textareaQuestionRef.current.style.height = 'auto';
|
||
textareaQuestionRef.current.style.height = `${textareaQuestionRef.current.scrollHeight}px`;
|
||
}
|
||
}, [isEditingQuestion]);
|
||
|
||
const handleAnswerChange = async (index: number, value: string) => {
|
||
const newAnswerVariants = [...initialAnswerVariants];
|
||
newAnswerVariants[index] = { ...newAnswerVariants[index], text: value };
|
||
onAnswerVariantsChange(newAnswerVariants);
|
||
|
||
// Обновляем на сервере только если вариант уже существует (имеет id)
|
||
if (surveyId && newAnswerVariants[index].id) {
|
||
try {
|
||
await updateAnswerVariant(
|
||
surveyId,
|
||
questionId,
|
||
newAnswerVariants[index].id!,
|
||
{ text: value }
|
||
);
|
||
} catch (error) {
|
||
console.error('Ошибка при обновлении варианта ответа:', error);
|
||
}
|
||
}
|
||
};
|
||
|
||
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 {
|
||
await onDeleteQuestion(questionId);
|
||
} catch (error) {
|
||
console.error('Ошибка при удалении вопроса:', error);
|
||
}
|
||
};
|
||
|
||
const toggleSelect = (index: number) => {
|
||
if (selectedType === 'single') {
|
||
setSelectedAnswers([index]);
|
||
} else {
|
||
setSelectedAnswers((prev) => {
|
||
if (prev.includes(index)) {
|
||
return prev.filter((i) => i !== index);
|
||
} else {
|
||
return [...prev, index];
|
||
}
|
||
});
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className={styles.questionCard}>
|
||
<div className={styles.questionContainer}>
|
||
<div className={styles.question}>
|
||
{isEditingQuestion ? (
|
||
<textarea
|
||
className={styles.questionTextarea}
|
||
ref={textareaQuestionRef}
|
||
value={textQuestion === initialTextQuestion ? '' : textQuestion}
|
||
onChange={handleTextareaQuestionChange}
|
||
onKeyDown={handleQuestionKeyDown}
|
||
onBlur={handleQuestionBlur}
|
||
placeholder={initialTextQuestion}
|
||
rows={1}
|
||
/>
|
||
) : (
|
||
<button className={styles.buttonQuestion} onClick={handleQuestionClick}>
|
||
<h2 className={styles.textQuestion}>{textQuestion || initialTextQuestion}</h2>
|
||
</button>
|
||
)}
|
||
<TypeDropdown selectedType={selectedType} onTypeChange={handleTypeChange}/>
|
||
</div>
|
||
|
||
{initialAnswerVariants.map((answer, index) => (
|
||
<AnswerOption
|
||
key={answer.id || index}
|
||
selectedType={selectedType}
|
||
index={index + 1}
|
||
value={answer.text}
|
||
onChange={(value) => handleAnswerChange(index, value)}
|
||
onDelete={() => handleDeleteAnswer(index)}
|
||
toggleSelect={() => toggleSelect(index)}
|
||
/>
|
||
))}
|
||
|
||
<div className={styles.questionActions}>
|
||
<AddAnswerButton onClick={handleAddAnswer} />
|
||
<button className={styles.deleteQuestionButton} onClick={handleDeleteQuestion}>
|
||
Удалить
|
||
<Delete className={styles.basketImg}/>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default QuestionItem; |