265 lines
No EOL
11 KiB
TypeScript
265 lines
No EOL
11 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/AnswerVariantsApi.ts";
|
||
import TextareaAutosize from "react-textarea-autosize";
|
||
import {useRouteReadOnly} from "../../hooks/useRouteReadOnly.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>;
|
||
initialQuestionType: 'SingleAnswerQuestion' | 'MultipleAnswerQuestion';
|
||
onQuestionTypeChange: (type: 'SingleAnswerQuestion' | 'MultipleAnswerQuestion') => void;
|
||
surveyId?: number;
|
||
onAnswerSelect?: (questionId: number, answerText: string) => void;
|
||
}
|
||
|
||
const QuestionItem: React.FC<QuestionItemProps> = ({
|
||
questionId,
|
||
initialTextQuestion = `Вопрос`,
|
||
valueQuestion,
|
||
answerVariants: initialAnswerVariants,
|
||
onChangeQuestion,
|
||
onAnswerVariantsChange,
|
||
onDeleteQuestion,
|
||
initialQuestionType,
|
||
onQuestionTypeChange,
|
||
surveyId,
|
||
onAnswerSelect
|
||
}) => {
|
||
const [textQuestion, setTextQuestion] = useState(initialTextQuestion);
|
||
const [isEditingQuestion, setIsEditingQuestion] = useState(false);
|
||
const [selectedAnswers, setSelectedAnswers] = useState<number[]>([]);
|
||
const [questionType, setQuestionType] = useState<'SingleAnswerQuestion' | 'MultipleAnswerQuestion'>(initialQuestionType);
|
||
const textareaQuestionRef = useRef<HTMLTextAreaElement>(null);
|
||
|
||
const isReadOnly = useRouteReadOnly();
|
||
|
||
|
||
useEffect(() => {
|
||
setTextQuestion(valueQuestion);
|
||
}, [valueQuestion]);
|
||
|
||
useEffect(() => {
|
||
setQuestionType(initialQuestionType);
|
||
}, [initialQuestionType]);
|
||
|
||
const handleTypeChange = (type: 'SingleAnswerQuestion' | 'MultipleAnswerQuestion') => {
|
||
setQuestionType(type);
|
||
onQuestionTypeChange(type);
|
||
};
|
||
|
||
const handleAddAnswer = async () => {
|
||
if (isReadOnly) return
|
||
|
||
if (!surveyId) {
|
||
onAnswerVariantsChange([...initialAnswerVariants, { text: '' }]);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const newAnswer = await addNewAnswerVariant(surveyId, questionId, { text: '' });
|
||
onAnswerVariantsChange([...initialAnswerVariants, {
|
||
id: newAnswer.id,
|
||
text: newAnswer.text
|
||
}]);
|
||
} catch (error) {
|
||
console.error('Ошибка при добавлении варианта ответа:', error);
|
||
}
|
||
};
|
||
|
||
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);
|
||
|
||
if (surveyId && newAnswerVariants[index].id) {
|
||
try {
|
||
await updateAnswerVariant(
|
||
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(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 (initialQuestionType === 'SingleAnswerQuestion') {
|
||
// setSelectedAnswers([index]);
|
||
// } else {
|
||
// setSelectedAnswers(prev =>
|
||
// prev.includes(index)
|
||
// ? prev.filter(i => i !== index)
|
||
// : [...prev, index]
|
||
// );
|
||
// }
|
||
// };
|
||
|
||
const toggleSelect = (index: number) => {
|
||
const answerText = initialAnswerVariants[index].text;
|
||
|
||
if (onAnswerSelect) {
|
||
onAnswerSelect(questionId, answerText);
|
||
}
|
||
|
||
if (initialQuestionType === 'SingleAnswerQuestion') {
|
||
setSelectedAnswers([index]);
|
||
} else {
|
||
setSelectedAnswers(prev =>
|
||
prev.includes(index)
|
||
? prev.filter(i => i !== index)
|
||
: [...prev, index]
|
||
);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className={styles.questionCard}>
|
||
{isReadOnly ? (
|
||
<div>
|
||
<div className={styles.questionContainer}>
|
||
<h2 className={styles.textQuestion}>{textQuestion || initialTextQuestion}</h2>
|
||
</div>
|
||
{initialAnswerVariants.map((answer, index) => (
|
||
<AnswerOption
|
||
key={answer.id || index}
|
||
selectedType={initialQuestionType}
|
||
index={index + 1}
|
||
value={answer.text}
|
||
isSelected={selectedAnswers.includes(index)}
|
||
toggleSelect={() => toggleSelect(index)}
|
||
/>
|
||
))}
|
||
</div>
|
||
) : (
|
||
<div className={styles.questionContainer}>
|
||
<div className={styles.question}>
|
||
{isEditingQuestion ? (
|
||
<TextareaAutosize
|
||
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={questionType} onTypeChange={handleTypeChange}/>
|
||
</div>
|
||
|
||
{initialAnswerVariants.map((answer, index) => (
|
||
<AnswerOption
|
||
key={answer.id || index}
|
||
selectedType={questionType}
|
||
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; |