add page completing survey
This commit is contained in:
parent
8182bc1c43
commit
4f1a7cc434
15 changed files with 333 additions and 120 deletions
|
|
@ -8,6 +8,8 @@ import {Results} from "./components/Results/Results.tsx";
|
||||||
import {MySurveyList} from "./components/MySurveyList/MySurveyList.tsx";
|
import {MySurveyList} from "./components/MySurveyList/MySurveyList.tsx";
|
||||||
import AuthForm from "./pages/AuthForm/AuthForm.tsx";
|
import AuthForm from "./pages/AuthForm/AuthForm.tsx";
|
||||||
import {SurveyPage} from "./components/SurveyPage/SurveyPage.tsx";
|
import {SurveyPage} from "./components/SurveyPage/SurveyPage.tsx";
|
||||||
|
import CompleteSurvey from "./pages/CompleteSurvey/CompleteSurvey.tsx";
|
||||||
|
import CompletingSurvey from "./components/CompletingSurvey/CompletingSurvey.tsx";
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return(
|
return(
|
||||||
|
|
@ -31,6 +33,10 @@ const App = () => {
|
||||||
<Route path="results" element={<Results />} />
|
<Route path="results" element={<Results />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
|
<Route path='/complete-survey' element={<CompleteSurvey/>}>
|
||||||
|
<Route index element={<CompletingSurvey/>}/>
|
||||||
|
</Route>
|
||||||
|
|
||||||
<Route path="*" element={<AuthForm />} />
|
<Route path="*" element={<AuthForm />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,22 @@ import styles from'./AnswerOption.module.css';
|
||||||
import Delete from '../../assets/delete.svg?react';
|
import Delete from '../../assets/delete.svg?react';
|
||||||
import Single from '../../assets/radio_button_unchecked.svg?react';
|
import Single from '../../assets/radio_button_unchecked.svg?react';
|
||||||
import Multiple from '../../assets/emptyCheckbox.svg?react';
|
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';
|
||||||
|
|
||||||
interface AnswerOptionProps{
|
interface AnswerOptionProps{
|
||||||
index: number;
|
index: number;
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (value: string) => void;
|
onChange?: (value: string) => void;
|
||||||
onDelete:(index: number) => void;
|
onDelete?:(index: number) => void;
|
||||||
selectedType: 'SingleAnswerQuestion' | 'MultipleAnswerQuestion';
|
selectedType: 'SingleAnswerQuestion' | 'MultipleAnswerQuestion';
|
||||||
toggleSelect: () => void;
|
isSelected?: boolean;
|
||||||
|
toggleSelect?: () => void;
|
||||||
|
isCompleteSurveyActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnswerOption: React.FC<AnswerOptionProps> = ({index, value, onChange, onDelete, selectedType, toggleSelect}) => {
|
const AnswerOption: React.FC<AnswerOptionProps> = ({index, value, onChange, onDelete, selectedType, isSelected, toggleSelect, isCompleteSurveyActive = false}) => {
|
||||||
const [currentValue, setCurrentValue] = useState(value);
|
const [currentValue, setCurrentValue] = useState(value);
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
|
|
@ -45,7 +50,7 @@ const AnswerOption: React.FC<AnswerOptionProps> = ({index, value, onChange, onDe
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
onChange(currentValue);
|
onChange?.(currentValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
|
@ -65,16 +70,50 @@ const AnswerOption: React.FC<AnswerOptionProps> = ({index, value, onChange, onDe
|
||||||
}
|
}
|
||||||
}, [isEditing]);
|
}, [isEditing]);
|
||||||
|
|
||||||
|
const handleMarkerClick = () => {
|
||||||
|
if (isCompleteSurveyActive && toggleSelect) {
|
||||||
|
toggleSelect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.answer}>
|
<div className={styles.answer}>
|
||||||
<button
|
{isCompleteSurveyActive ? (
|
||||||
className={`${styles.buttonMarker} ${isEditing ? styles.editing : ''}`}
|
<button
|
||||||
onClick={toggleSelect}
|
className={`${styles.buttonMarker} ${isSelected ? styles.selected : ''}`}
|
||||||
>
|
onClick={handleMarkerClick}
|
||||||
{selectedType === 'SingleAnswerQuestion' ? < Single className={styles.answerIcon} /> : <Multiple className={styles.answerIcon} />}
|
>
|
||||||
</button>
|
{selectedType === 'SingleAnswerQuestion' ? (
|
||||||
{isEditing ? (
|
isSelected ? (
|
||||||
<textarea
|
<SelectedSingle className={styles.answerIcon} />
|
||||||
|
) : (
|
||||||
|
<Single className={styles.answerIcon} />
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
isSelected ? (
|
||||||
|
<SelectedMultiple className={styles.answerIcon} />
|
||||||
|
) : (
|
||||||
|
<Multiple className={styles.answerIcon} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button className={styles.buttonMarker}>
|
||||||
|
{selectedType === 'SingleAnswerQuestion' ? (
|
||||||
|
<Single className={styles.answerIcon} />
|
||||||
|
) : (
|
||||||
|
<Multiple className={styles.answerIcon} />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isCompleteSurveyActive ? (
|
||||||
|
<button className={styles.textAnswer}>
|
||||||
|
{currentValue || `Ответ ${index}`}
|
||||||
|
</button>
|
||||||
|
) : isEditing ? (
|
||||||
|
<TextareaAutosize
|
||||||
className={styles.answerInput}
|
className={styles.answerInput}
|
||||||
ref={textAreaRef}
|
ref={textAreaRef}
|
||||||
value={currentValue}
|
value={currentValue}
|
||||||
|
|
@ -88,12 +127,14 @@ const AnswerOption: React.FC<AnswerOptionProps> = ({index, value, onChange, onDe
|
||||||
{currentValue || `Ответ ${index}`}
|
{currentValue || `Ответ ${index}`}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button className={styles.deleteButton} onClick={() => onDelete(index)}>
|
|
||||||
<Delete />
|
{!isCompleteSurveyActive && (
|
||||||
</button>
|
<button className={styles.deleteButton} onClick={() => onDelete?.(index)}>
|
||||||
|
<Delete />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AnswerOption;
|
export default AnswerOption;
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
.survey{
|
||||||
|
width: 68%;
|
||||||
|
background-color: #F6F6F6;
|
||||||
|
max-width: 100vw;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 34px 16%;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx";
|
||||||
|
import QuestionsList, {Question} from "../QuestionsList/QuestionsList.tsx";
|
||||||
|
import {useState} from "react";
|
||||||
|
import styles from './CompletingSurvey.module.css'
|
||||||
|
|
||||||
|
export const CompletingSurvey = () => {
|
||||||
|
const [titleSurvey, setTitleSurvey] = useState("Название опроса");
|
||||||
|
const [descriptionSurvey, setDescriptionSurvey] = useState("");
|
||||||
|
const [questions, setQuestions] = useState<Question[]>([
|
||||||
|
{ 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' }]}
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.survey}>
|
||||||
|
<SurveyInfo
|
||||||
|
titleSurvey={titleSurvey}
|
||||||
|
descriptionSurvey={descriptionSurvey}
|
||||||
|
setDescriptionSurvey={setDescriptionSurvey}
|
||||||
|
setTitleSurvey={setTitleSurvey}
|
||||||
|
/>
|
||||||
|
<QuestionsList
|
||||||
|
questions={questions}
|
||||||
|
setQuestions={setQuestions}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CompletingSurvey
|
||||||
|
|
@ -12,8 +12,7 @@
|
||||||
gap: 60px;
|
gap: 60px;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-right: 40%;
|
margin-right: 20%;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.pageLink{
|
.pageLink{
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,13 @@ import {Link, useLocation, useNavigate} from "react-router-dom";
|
||||||
const Header: React.FC = () => {
|
const Header: React.FC = () => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const isCreateSurveyActive = location.pathname.includes('/survey/create');
|
|
||||||
const isSurveyPage = location.pathname.includes('/survey/') && !location.pathname.includes('/survey/create');
|
const isCreateSurveyActive = location.pathname.startsWith('/survey/create');
|
||||||
const isMySurveysPage = location.pathname === '/my-surveys' || isSurveyPage;
|
const isMySurveysActive = location.pathname === '/my-surveys';
|
||||||
|
const isCompleteSurveyActive = location.pathname === '/complete-survey';
|
||||||
|
|
||||||
|
const isSurveyViewPage = location.pathname.startsWith('/survey/') &&
|
||||||
|
!location.pathname.startsWith('/survey/create');
|
||||||
|
|
||||||
const handleLogoClick = () => {
|
const handleLogoClick = () => {
|
||||||
navigate(location.pathname, { replace: true });
|
navigate(location.pathname, { replace: true });
|
||||||
|
|
@ -19,15 +23,26 @@ const Header: React.FC = () => {
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<Logo href={location.pathname} onClick={handleLogoClick} />
|
<Logo href={location.pathname} onClick={handleLogoClick} />
|
||||||
<nav className={styles.pagesNav}>
|
<nav className={styles.pagesNav}>
|
||||||
<Link to='/survey/create/questions'
|
<Link
|
||||||
className={`${styles.pageLink} ${isCreateSurveyActive ? styles.active : ''}`}>
|
to='/survey/create/questions'
|
||||||
|
className={`${styles.pageLink} ${isCreateSurveyActive ? styles.active : ''}`}
|
||||||
|
>
|
||||||
Создать опрос
|
Создать опрос
|
||||||
{isCreateSurveyActive && <hr className={styles.activeLine}/>}
|
{isCreateSurveyActive && <hr className={styles.activeLine}/>}
|
||||||
</Link>
|
</Link>
|
||||||
<Link to='/my-surveys'
|
<Link
|
||||||
className={`${styles.pageLink} ${isMySurveysPage ? styles.active : ''}`}>
|
to='/my-surveys'
|
||||||
|
className={`${styles.pageLink} ${isMySurveysActive || isSurveyViewPage ? styles.active : ''}`}
|
||||||
|
>
|
||||||
Мои опросы
|
Мои опросы
|
||||||
{isMySurveysPage && <hr className={styles.activeLine}/>}
|
{(isMySurveysActive || isSurveyViewPage) && <hr className={styles.activeLine}/>}
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to='/complete-survey'
|
||||||
|
className={`${styles.pageLink} ${isCompleteSurveyActive ? styles.active : ''}`}
|
||||||
|
>
|
||||||
|
Прохождение опроса
|
||||||
|
{isCompleteSurveyActive && <hr className={styles.activeLine}/>}
|
||||||
</Link>
|
</Link>
|
||||||
</nav>
|
</nav>
|
||||||
<Account href={'/profile'} />
|
<Account href={'/profile'} />
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
.main {
|
.main {
|
||||||
background-color: #F6F6F6;
|
background-color: #F6F6F6;
|
||||||
width: 100%;
|
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 34px 10%;
|
padding-top: 34px;
|
||||||
|
padding-left: 12%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.survey {
|
.survey {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import {
|
||||||
getAnswerVariants,
|
getAnswerVariants,
|
||||||
updateAnswerVariant
|
updateAnswerVariant
|
||||||
} from "../../api/AnswerApi.ts";
|
} from "../../api/AnswerApi.ts";
|
||||||
|
import {useLocation} from "react-router-dom";
|
||||||
|
import TextareaAutosize from "react-textarea-autosize";
|
||||||
|
|
||||||
interface QuestionItemProps {
|
interface QuestionItemProps {
|
||||||
questionId: number;
|
questionId: number;
|
||||||
|
|
@ -43,6 +45,10 @@ const QuestionItem: React.FC<QuestionItemProps> = ({
|
||||||
const [questionType, setQuestionType] = useState<'SingleAnswerQuestion' | 'MultipleAnswerQuestion'>(initialQuestionType);
|
const [questionType, setQuestionType] = useState<'SingleAnswerQuestion' | 'MultipleAnswerQuestion'>(initialQuestionType);
|
||||||
const textareaQuestionRef = useRef<HTMLTextAreaElement>(null);
|
const textareaQuestionRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
const isCompleteSurveyActive = location.pathname === '/complete-survey';
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTextQuestion(valueQuestion);
|
setTextQuestion(valueQuestion);
|
||||||
}, [valueQuestion]);
|
}, [valueQuestion]);
|
||||||
|
|
@ -162,62 +168,81 @@ const QuestionItem: React.FC<QuestionItemProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleSelect = (index: number) => {
|
const toggleSelect = (index: number) => {
|
||||||
if (questionType === 'SingleAnswerQuestion') {
|
if (initialQuestionType === 'SingleAnswerQuestion') {
|
||||||
|
// Для одиночного выбора: заменяем массив одним выбранным индексом
|
||||||
setSelectedAnswers([index]);
|
setSelectedAnswers([index]);
|
||||||
} else {
|
} else {
|
||||||
setSelectedAnswers((prev) => {
|
// Для множественного выбора: добавляем/удаляем индекс
|
||||||
if (prev.includes(index)) {
|
setSelectedAnswers(prev =>
|
||||||
return prev.filter((i) => i !== index);
|
prev.includes(index)
|
||||||
} else {
|
? prev.filter(i => i !== index)
|
||||||
return [...prev, index];
|
: [...prev, index]
|
||||||
}
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.questionCard}>
|
<div className={styles.questionCard}>
|
||||||
<div className={styles.questionContainer}>
|
{isCompleteSurveyActive ? (
|
||||||
<div className={styles.question}>
|
<div>
|
||||||
{isEditingQuestion ? (
|
<div className={styles.questionContainer}>
|
||||||
<textarea
|
<h2 className={styles.textQuestion}>{textQuestion || initialTextQuestion}</h2>
|
||||||
className={styles.questionTextarea}
|
</div>
|
||||||
ref={textareaQuestionRef}
|
{initialAnswerVariants.map((answer, index) => (
|
||||||
value={textQuestion === initialTextQuestion ? '' : textQuestion}
|
<AnswerOption
|
||||||
onChange={handleTextareaQuestionChange}
|
key={answer.id || index}
|
||||||
onKeyDown={handleQuestionKeyDown}
|
selectedType={initialQuestionType}
|
||||||
onBlur={handleQuestionBlur}
|
index={index + 1}
|
||||||
placeholder={initialTextQuestion}
|
value={answer.text}
|
||||||
rows={1}
|
isSelected={selectedAnswers.includes(index)}
|
||||||
|
toggleSelect={() => toggleSelect(index)}
|
||||||
|
isCompleteSurveyActive={isCompleteSurveyActive}
|
||||||
/>
|
/>
|
||||||
) : (
|
))}
|
||||||
<button className={styles.buttonQuestion} onClick={handleQuestionClick}>
|
|
||||||
<h2 className={styles.textQuestion}>{textQuestion || initialTextQuestion}</h2>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<TypeDropdown selectedType={questionType} onTypeChange={handleTypeChange}/>
|
|
||||||
</div>
|
</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) => (
|
{initialAnswerVariants.map((answer, index) => (
|
||||||
<AnswerOption
|
<AnswerOption
|
||||||
key={answer.id || index}
|
key={answer.id || index}
|
||||||
selectedType={questionType}
|
selectedType={questionType}
|
||||||
index={index + 1}
|
index={index + 1}
|
||||||
value={answer.text}
|
value={answer.text}
|
||||||
onChange={(value) => handleAnswerChange(index, value)}
|
onChange={(value) => handleAnswerChange(index, value)}
|
||||||
onDelete={() => handleDeleteAnswer(index)}
|
onDelete={() => handleDeleteAnswer(index)}
|
||||||
toggleSelect={() => toggleSelect(index)}
|
toggleSelect={() => toggleSelect(index)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<div className={styles.questionActions}>
|
<div className={styles.questionActions}>
|
||||||
<AddAnswerButton onClick={handleAddAnswer} />
|
<AddAnswerButton onClick={handleAddAnswer} />
|
||||||
<button className={styles.deleteQuestionButton} onClick={handleDeleteQuestion}>
|
<button className={styles.deleteQuestionButton} onClick={handleDeleteQuestion}>
|
||||||
Удалить
|
Удалить
|
||||||
<Delete className={styles.basketImg}/>
|
<Delete className={styles.basketImg}/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,16 @@
|
||||||
/*QuestionsList.module.css*/
|
/*QuestionsList.module.css*/
|
||||||
|
|
||||||
|
.departur_button{
|
||||||
|
display: block;
|
||||||
|
margin: 10px auto;
|
||||||
|
padding: 25px 50.5px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 20px;
|
||||||
|
background-color: #3788D6;
|
||||||
|
color: white;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 0 7.4px 0 rgba(154, 202, 247, 1);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,8 @@ import QuestionItem from "../QuestionItem/QuestionItem.tsx";
|
||||||
import AddQuestionButton from "../AddQuestionButton/AddQuestionButton.tsx";
|
import AddQuestionButton from "../AddQuestionButton/AddQuestionButton.tsx";
|
||||||
import {addNewQuestion, deleteQuestion, getListQuestions} from "../../api/QuestionApi.ts";
|
import {addNewQuestion, deleteQuestion, getListQuestions} from "../../api/QuestionApi.ts";
|
||||||
import {addNewAnswerVariant} from "../../api/AnswerApi.ts";
|
import {addNewAnswerVariant} from "../../api/AnswerApi.ts";
|
||||||
|
import {useLocation} from "react-router-dom";
|
||||||
|
import styles from './QuestionsList.module.css'
|
||||||
|
|
||||||
interface QuestionsListProps {
|
interface QuestionsListProps {
|
||||||
questions: Question[];
|
questions: Question[];
|
||||||
|
|
@ -21,6 +23,9 @@ export interface Question {
|
||||||
}
|
}
|
||||||
|
|
||||||
const QuestionsList: React.FC<QuestionsListProps> = ({questions, setQuestions, surveyId}) => {
|
const QuestionsList: React.FC<QuestionsListProps> = ({questions, setQuestions, surveyId}) => {
|
||||||
|
const location = useLocation();
|
||||||
|
const isCompleteSurveyActive = location.pathname === '/complete-survey';
|
||||||
|
|
||||||
const handleAddQuestion = async () => {
|
const handleAddQuestion = async () => {
|
||||||
if (!surveyId) {
|
if (!surveyId) {
|
||||||
const newQuestion: Question = {
|
const newQuestion: Question = {
|
||||||
|
|
@ -123,7 +128,10 @@ const QuestionsList: React.FC<QuestionsListProps> = ({questions, setQuestions, s
|
||||||
surveyId={surveyId}
|
surveyId={surveyId}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<AddQuestionButton onClick={handleAddQuestion} />
|
{!isCompleteSurveyActive ? <AddQuestionButton onClick={handleAddQuestion} /> : (
|
||||||
|
<button className={styles.departur_button}>Отправить</button>
|
||||||
|
)}
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,37 +7,45 @@
|
||||||
margin-top: 34px;
|
margin-top: 34px;
|
||||||
margin-bottom: 49px;
|
margin-bottom: 49px;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
min-height: 191px;
|
/*min-height: 191px;*/
|
||||||
|
/*max-height: 100vh;*/
|
||||||
|
max-height: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.info{
|
.info{
|
||||||
min-width: 373px;
|
min-width: 373px;
|
||||||
display: block;
|
/*display: block;*/
|
||||||
padding: 35px;
|
padding: 35px;
|
||||||
|
display: flex; /* Добавляем flex */
|
||||||
|
flex-direction: column; /* Элементы в колонку */
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.titleSurvey{
|
.titleSurvey{
|
||||||
|
width: 80%;
|
||||||
display: block;
|
display: block;
|
||||||
border: none;
|
border: none;
|
||||||
margin: 0 auto;
|
margin: 0 auto 13px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 23px;
|
/*margin-bottom: 23px;*/
|
||||||
|
/*margin-bottom: 15px;*/
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textareaTitle,
|
.textareaTitle,
|
||||||
.textareaDescrip {
|
.textareaDescrip {
|
||||||
width: 100%;
|
width: 80%;
|
||||||
|
max-width: 100%;
|
||||||
resize: none;
|
resize: none;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0 auto;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
display: block;
|
display: block;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
|
@ -48,7 +56,7 @@
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
min-height: 40px;
|
min-height: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textareaDescrip {
|
.textareaDescrip {
|
||||||
|
|
@ -67,7 +75,7 @@
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
border: none;
|
border: none;
|
||||||
font-size: 18px;
|
font-size: 24px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
@ -78,6 +86,15 @@
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.desc{
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
background-color: white;
|
||||||
|
max-width: 80%;
|
||||||
|
word-break: break-word;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.descripButton{
|
.descripButton{
|
||||||
border: none;
|
border: none;
|
||||||
|
|
@ -97,4 +114,11 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #7D7983;
|
color: #7D7983;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.createdAt{
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #7D7983;
|
||||||
}
|
}
|
||||||
|
|
@ -2,27 +2,34 @@ import React, {useState, useRef, useEffect} from "react";
|
||||||
import styles from './SurveyInfo.module.css'
|
import styles from './SurveyInfo.module.css'
|
||||||
import AddDescripImg from '../../assets/add_circle.svg?react';
|
import AddDescripImg from '../../assets/add_circle.svg?react';
|
||||||
import TextareaAutosize from 'react-textarea-autosize';
|
import TextareaAutosize from 'react-textarea-autosize';
|
||||||
|
import {useLocation} from "react-router-dom";
|
||||||
|
|
||||||
|
|
||||||
interface SurveyInfoProps {
|
interface SurveyInfoProps {
|
||||||
titleSurvey: string;
|
titleSurvey: string;
|
||||||
descriptionSurvey: string;
|
descriptionSurvey: string;
|
||||||
setDescriptionSurvey: (text: string) => void;
|
setDescriptionSurvey?: (text: string) => void;
|
||||||
setTitleSurvey: (text: string) => void;
|
setTitleSurvey?: (text: string) => void;
|
||||||
|
createdAt?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SurveyInfo: React.FC<SurveyInfoProps> = ({titleSurvey, setDescriptionSurvey, descriptionSurvey, setTitleSurvey}) => {
|
const SurveyInfo: React.FC<SurveyInfoProps> = ({titleSurvey, setDescriptionSurvey, descriptionSurvey, setTitleSurvey, createdAt = new Date()}) => {
|
||||||
const [showDescriptionField, setShowDescriptionField] = useState(false);
|
const [showDescriptionField, setShowDescriptionField] = useState(false);
|
||||||
const [showNewTitleField, setShowNewTitleField] = useState(false);
|
const [showNewTitleField, setShowNewTitleField] = useState(false);
|
||||||
const titleTextareaRef = useRef<HTMLTextAreaElement>(null);
|
const titleTextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
const descriptionTextareaRef = useRef<HTMLTextAreaElement>(null);
|
const descriptionTextareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
const isCompleteSurveyActive = location.pathname === '/complete-survey';
|
||||||
|
const isSurveyViewPage = location.pathname.startsWith('/survey/') &&
|
||||||
|
!location.pathname.startsWith('/survey/create');
|
||||||
|
|
||||||
const handleDescriptionChange = (descripEvent: React.ChangeEvent<HTMLTextAreaElement>) => {
|
const handleDescriptionChange = (descripEvent: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
setDescriptionSurvey(descripEvent.target.value);
|
setDescriptionSurvey?.(descripEvent.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNewTitleChange = (titleEvent: React.ChangeEvent<HTMLTextAreaElement>) => {
|
const handleNewTitleChange = (titleEvent: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
setTitleSurvey(titleEvent.target.value);
|
setTitleSurvey?.(titleEvent.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -72,8 +79,52 @@ const SurveyInfo: React.FC<SurveyInfoProps> = ({titleSurvey, setDescriptionSurve
|
||||||
setShowDescriptionField(false);
|
setShowDescriptionField(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addDate = () => {
|
||||||
|
const year = createdAt.getFullYear();
|
||||||
|
const month = String(createdAt.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(createdAt.getDate()).padStart(2, '0');
|
||||||
|
return `${day}/${month}/${year}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderTitle = () => {
|
||||||
|
if (isSurveyViewPage || isCompleteSurveyActive) {
|
||||||
|
return (
|
||||||
|
<button className={styles.titleSurvey}>
|
||||||
|
<h1>{titleSurvey || 'Название опроса'}</h1>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showNewTitleField) {
|
||||||
|
return (
|
||||||
|
<h1 className={styles.titleSurvey}>
|
||||||
|
<TextareaAutosize
|
||||||
|
className={styles.textareaTitle}
|
||||||
|
ref={titleTextareaRef}
|
||||||
|
value={titleSurvey === 'Название опроса' ? '' : titleSurvey}
|
||||||
|
placeholder={'Название опроса'}
|
||||||
|
onChange={handleNewTitleChange}
|
||||||
|
onKeyDown={handleTitleKeyDown}
|
||||||
|
onBlur={handleTitleBlur}
|
||||||
|
/>
|
||||||
|
</h1>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button className={styles.titleSurvey} onClick={handleAddNewTitleClick}>
|
||||||
|
<h1>{titleSurvey || 'Название опроса'}</h1>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const renderDescription = () => {
|
const renderDescription = () => {
|
||||||
|
if (isSurveyViewPage || isCompleteSurveyActive) {
|
||||||
|
return descriptionSurvey ? (
|
||||||
|
<p className={styles.desc}>{descriptionSurvey}</p>
|
||||||
|
) : 'Описание';
|
||||||
|
}
|
||||||
|
|
||||||
if (descriptionSurvey && !showDescriptionField) {
|
if (descriptionSurvey && !showDescriptionField) {
|
||||||
return (
|
return (
|
||||||
<button className={styles.description} onClick={handleParagraphClick}>
|
<button className={styles.description} onClick={handleParagraphClick}>
|
||||||
|
|
@ -106,35 +157,21 @@ const SurveyInfo: React.FC<SurveyInfoProps> = ({titleSurvey, setDescriptionSurve
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.blockInfo}>
|
<div className={styles.blockInfo}>
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>
|
||||||
{
|
{renderTitle()}
|
||||||
showNewTitleField ? (
|
|
||||||
<h1 className={styles.titleSurvey}>
|
|
||||||
<TextareaAutosize className={styles.textareaTitle}
|
|
||||||
ref={titleTextareaRef}
|
|
||||||
value={titleSurvey === 'Название опроса' ? '' : titleSurvey}
|
|
||||||
placeholder={'Название опроса'}
|
|
||||||
onChange={handleNewTitleChange}
|
|
||||||
onKeyDown={handleTitleKeyDown}
|
|
||||||
onBlur={handleTitleBlur}
|
|
||||||
/>
|
|
||||||
</h1>
|
|
||||||
) : (
|
|
||||||
<button className={styles.titleSurvey} onClick={handleAddNewTitleClick}>
|
|
||||||
<h1>{titleSurvey || 'Название опроса'}</h1>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
{renderDescription()}
|
{renderDescription()}
|
||||||
|
|
||||||
|
{(isSurveyViewPage || isCompleteSurveyActive) && createdAt && (
|
||||||
|
<p className={styles.createdAt}>Дата создания: {addDate()}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SurveyInfo;
|
export default SurveyInfo;
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
.layout{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
14
SurveyFrontend/src/pages/CompleteSurvey/CompleteSurvey.tsx
Normal file
14
SurveyFrontend/src/pages/CompleteSurvey/CompleteSurvey.tsx
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import Header from "../../components/Header/Header.tsx";
|
||||||
|
import styles from './CompleteSurvey.module.css'
|
||||||
|
import CompletingSurvey from "../../components/CompletingSurvey/CompletingSurvey.tsx";
|
||||||
|
|
||||||
|
export const CompleteSurvey = () => {
|
||||||
|
return(
|
||||||
|
<div className={styles.layout}>
|
||||||
|
<Header/>
|
||||||
|
<CompletingSurvey/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CompleteSurvey
|
||||||
|
|
@ -2,16 +2,4 @@
|
||||||
|
|
||||||
.layout{
|
.layout{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
.main{
|
|
||||||
width: 100%;
|
|
||||||
min-height: 85vh;
|
|
||||||
display: flex;
|
|
||||||
background-color: #F6F6F6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content{
|
|
||||||
width: 100%;
|
|
||||||
margin-left: 8.9%;
|
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue