Merge branch 'correction' into 'unstable'
fix results page See merge request internship-2025/survey-webapp/survey-webapp!30
This commit is contained in:
commit
d93c7ab3c9
9 changed files with 267 additions and 136 deletions
|
|
@ -11,6 +11,10 @@ export const handleUnauthorizedError = (error: unknown) => {
|
||||||
window.location.href = '/login';
|
window.location.href = '/login';
|
||||||
console.log('Сессия истекла. Перенаправление на страницу входа.');
|
console.log('Сессия истекла. Перенаправление на страницу входа.');
|
||||||
}
|
}
|
||||||
|
if (error instanceof Error && error.message.includes('Токен отсутствует')) {
|
||||||
|
window.location.href = '/login';
|
||||||
|
console.log('Сессия истекла. Перенаправление на страницу входа.');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,8 @@ export const updateQuestion = async (id: number, question: Partial<INewQuestion>
|
||||||
questionType: question.questionType,
|
questionType: question.questionType,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
return await handleResponse(response)
|
const result = await handleResponse(response);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
catch(error){
|
catch(error){
|
||||||
handleUnauthorizedError(error);
|
handleUnauthorizedError(error);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||||
import styles from './Account.module.css';
|
import styles from './Account.module.css';
|
||||||
import AccountImg from '../../assets/account.svg?react';
|
import AccountImg from '../../assets/account.svg?react';
|
||||||
import { getCurrentUser } from '../../api/AuthApi';
|
import { getCurrentUser } from '../../api/AuthApi';
|
||||||
|
import {handleUnauthorizedError} from "../../api/BaseApi.ts";
|
||||||
|
|
||||||
interface AccountProps {
|
interface AccountProps {
|
||||||
href: string;
|
href: string;
|
||||||
|
|
@ -17,6 +18,7 @@ const Account: React.FC<AccountProps> = ({ href }) => {
|
||||||
const userData = await getCurrentUser();
|
const userData = await getCurrentUser();
|
||||||
setUserName(`${userData.firstName} ${userData.lastName}`);
|
setUserName(`${userData.firstName} ${userData.lastName}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
handleUnauthorizedError(error)
|
||||||
console.error("Ошибка загрузки данных пользователя:", error);
|
console.error("Ошибка загрузки данных пользователя:", error);
|
||||||
localStorage.removeItem("user");
|
localStorage.removeItem("user");
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,9 @@ const QuestionItem: React.FC<QuestionItemProps> = ({
|
||||||
setTextQuestion(valueQuestion);
|
setTextQuestion(valueQuestion);
|
||||||
}, [valueQuestion]);
|
}, [valueQuestion]);
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
setQuestionType(initialQuestionType);
|
// setQuestionType(initialQuestionType);
|
||||||
}, [initialQuestionType]);
|
// }, [initialQuestionType]);
|
||||||
|
|
||||||
const handleTypeChange = (type: 'SingleAnswerQuestion' | 'MultipleAnswerQuestion') => {
|
const handleTypeChange = (type: 'SingleAnswerQuestion' | 'MultipleAnswerQuestion') => {
|
||||||
setQuestionType(type);
|
setQuestionType(type);
|
||||||
|
|
@ -167,18 +167,6 @@ const QuestionItem: React.FC<QuestionItemProps> = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 toggleSelect = (index: number) => {
|
||||||
const answerText = initialAnswerVariants[index].text;
|
const answerText = initialAnswerVariants[index].text;
|
||||||
|
|
||||||
|
|
@ -234,7 +222,13 @@ const QuestionItem: React.FC<QuestionItemProps> = ({
|
||||||
<h2 className={styles.textQuestion}>{textQuestion || initialTextQuestion}</h2>
|
<h2 className={styles.textQuestion}>{textQuestion || initialTextQuestion}</h2>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<TypeDropdown selectedType={questionType} onTypeChange={handleTypeChange}/>
|
|
||||||
|
<TypeDropdown
|
||||||
|
selectedType={questionType}
|
||||||
|
onTypeChange={(type) => {
|
||||||
|
handleTypeChange(type);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{initialAnswerVariants.map((answer, index) => (
|
{initialAnswerVariants.map((answer, index) => (
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,6 @@
|
||||||
|
|
||||||
.exportButtonContainer {
|
.exportButtonContainer {
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
/*background-color: #4CAF50;*/
|
|
||||||
background-color: #3788D6;
|
background-color: #3788D6;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
@ -159,5 +158,52 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.exportButtonContainer:hover {
|
.exportButtonContainer:hover {
|
||||||
background-color: #45a049;
|
background-color: rgb(14, 122, 221);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info{
|
||||||
|
margin-top: 60px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exportButtonWrapper {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exportPopup {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
|
color: #333;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
white-space: nowrap;
|
||||||
|
animation: fadeInOut 2s ease-in-out;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInOut {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50%) translateY(5px);
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50%) translateY(5px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ import Group from '../../assets/gmail_groups.svg?react';
|
||||||
import Send from '../../assets/send.svg?react';
|
import Send from '../../assets/send.svg?react';
|
||||||
import {getAllCompletions} from "../../api/CompletionApi.ts";
|
import {getAllCompletions} from "../../api/CompletionApi.ts";
|
||||||
import {getAnswer} from "../../api/AnswerApi.ts";
|
import {getAnswer} from "../../api/AnswerApi.ts";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useRef, useState} from "react";
|
||||||
import {getListQuestions} from "../../api/QuestionApi.ts";
|
import {getListQuestions} from "../../api/QuestionApi.ts";
|
||||||
import {getAnswerVariants, IAnswerVariant} from "../../api/AnswerVariantsApi.ts";
|
import {getAnswerVariants, IAnswerVariant} from "../../api/AnswerVariantsApi.ts";
|
||||||
import {getResultsFile} from "../../api/ExportResultApi.ts";
|
import {getResultsFile} from "../../api/ExportResultApi.ts";
|
||||||
|
|
@ -52,10 +52,18 @@ export const Results = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const [survey, setLocalSurvey] = useState<ISurvey>(initialSurvey);
|
const [survey, setLocalSurvey] = useState<ISurvey>(initialSurvey);
|
||||||
// const [questions, setQuestions] = useState<IQuestion[]>([]);
|
|
||||||
|
const [showExportPopup, setShowExportPopup] = useState(false);
|
||||||
|
const exportButtonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
const handleExportToExcel = async (id: number) => {
|
const handleExportToExcel = async (id: number) => {
|
||||||
await getResultsFile(id)
|
try {
|
||||||
|
await getResultsFile(id);
|
||||||
|
setShowExportPopup(true);
|
||||||
|
setTimeout(() => setShowExportPopup(false), 2000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при экспорте:', error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -65,7 +73,6 @@ export const Results = () => {
|
||||||
setLocalSurvey(surveyData);
|
setLocalSurvey(surveyData);
|
||||||
|
|
||||||
const questionsList = await getListQuestions(survey.id);
|
const questionsList = await getListQuestions(survey.id);
|
||||||
// setQuestions(questionsList);
|
|
||||||
|
|
||||||
const questionsWithVariants = await Promise.all(
|
const questionsWithVariants = await Promise.all(
|
||||||
questionsList.map(async (question) => {
|
questionsList.map(async (question) => {
|
||||||
|
|
@ -205,116 +212,134 @@ export const Results = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{surveyStats.questions.map((question, index) => (
|
{surveyStats.totalParticipants === 0 ? (
|
||||||
<div key={index} className={styles.questionContainer}>
|
<p className={styles.info}>Нет данных о прохождении опроса</p>
|
||||||
<div className={styles.questionContent}>
|
) : (
|
||||||
<div className={styles.textContainer}>
|
surveyStats.questions.map((question, index) => (
|
||||||
<h3>{question.questionText}</h3>
|
<div key={index} className={styles.questionContainer}>
|
||||||
<p className={styles.answerCount}>Ответов: {question.totalAnswers}</p>
|
<div className={styles.questionContent}>
|
||||||
</div>
|
<div className={styles.textContainer}>
|
||||||
|
<h3>{question.questionText}</h3>
|
||||||
|
<p className={styles.answerCount}>Ответов: {question.totalAnswers}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className={styles.chartContainer}>
|
<div className={styles.chartContainer}>
|
||||||
{question.isMultipleChoice ? (
|
{question.isMultipleChoice ? (
|
||||||
<div className={styles.barContainer}>
|
<div className={styles.barContainer}>
|
||||||
<Bar
|
<Bar
|
||||||
data={{
|
data={{
|
||||||
labels: question.options.map(opt => opt.text),
|
labels: question.options.map(opt => opt.text),
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: '% выбравших',
|
label: '% выбравших',
|
||||||
data: question.options.map(opt => opt.percentage),
|
data: question.options.map(opt => opt.percentage),
|
||||||
backgroundColor: colorsForBar,
|
backgroundColor: colorsForBar,
|
||||||
borderColor: colorsForBar,
|
borderColor: colorsForBar,
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
borderSkipped: false,
|
borderSkipped: false,
|
||||||
}]
|
}]
|
||||||
}}
|
}}
|
||||||
options={{
|
options={{
|
||||||
responsive: true,
|
responsive: true,
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: {
|
||||||
display: false
|
display: false
|
||||||
},
|
},
|
||||||
tooltip: { enabled: true },
|
tooltip: { enabled: true },
|
||||||
datalabels: { display: false },
|
datalabels: { display: false },
|
||||||
annotation: {
|
annotation: {
|
||||||
annotations: question.options.map((opt, i) => ({
|
annotations: question.options.map((opt, i) => ({
|
||||||
type: 'label',
|
type: 'label',
|
||||||
xValue: i,
|
xValue: i,
|
||||||
yValue: opt.percentage + 5,
|
yValue: opt.percentage + 5,
|
||||||
content: `${opt.percentage}%`,
|
content: `${opt.percentage}%`,
|
||||||
font: { size: 1, weight: 400 },
|
font: { size: 1, weight: 400 },
|
||||||
color: '#000'
|
color: '#000'
|
||||||
}))
|
}))
|
||||||
}
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
max: 100,
|
|
||||||
ticks: { callback: (val) => `${val}%` }
|
|
||||||
},
|
|
||||||
x: {
|
|
||||||
ticks: {
|
|
||||||
color: '#000000',
|
|
||||||
font: {
|
|
||||||
size: 12,
|
|
||||||
weight: 400
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
grid: { display: false }
|
scales: {
|
||||||
}
|
y: {
|
||||||
}
|
beginAtZero: true,
|
||||||
}}
|
max: 100,
|
||||||
/>
|
ticks: { callback: (val) => `${val}%` }
|
||||||
</div>
|
},
|
||||||
) : (
|
x: {
|
||||||
<div className={styles.pieContainer}>
|
ticks: {
|
||||||
<Pie
|
color: '#000000',
|
||||||
data={{
|
font: {
|
||||||
labels: question.options.map(opt => opt.text),
|
size: 12,
|
||||||
datasets: [{
|
weight: 400
|
||||||
data: question.options.map(opt => opt.percentage),
|
}
|
||||||
backgroundColor: colorsForPie,
|
},
|
||||||
borderColor: '#fff',
|
grid: { display: false }
|
||||||
borderWidth: 2
|
|
||||||
}]
|
|
||||||
}}
|
|
||||||
options={{
|
|
||||||
responsive: true,
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
position: 'right',
|
|
||||||
labels: {
|
|
||||||
color: '#000000',
|
|
||||||
font: {
|
|
||||||
size: 12,
|
|
||||||
weight: 500
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}}
|
||||||
tooltip: {
|
/>
|
||||||
callbacks: {
|
</div>
|
||||||
label: (ctx) => `${ctx.label}: ${ctx.raw}%`
|
) : (
|
||||||
}
|
<div className={styles.pieContainer}>
|
||||||
},
|
<Pie
|
||||||
datalabels: {
|
data={{
|
||||||
formatter: (value) => `${value}%`,
|
labels: question.options.map(opt => opt.text),
|
||||||
color: '#000',
|
datasets: [{
|
||||||
font: { weight: 400, size: 12 }
|
data: question.options.map(opt => opt.percentage),
|
||||||
}
|
backgroundColor: colorsForPie,
|
||||||
},
|
borderColor: '#fff',
|
||||||
animation: { animateRotate: true }
|
borderWidth: 2
|
||||||
}}
|
}]
|
||||||
/>
|
}}
|
||||||
|
options={{
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'right',
|
||||||
|
labels: {
|
||||||
|
color: '#000000',
|
||||||
|
font: {
|
||||||
|
size: 12,
|
||||||
|
weight: 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: (ctx) => `${ctx.label}: ${ctx.raw}%`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
datalabels: {
|
||||||
|
formatter: (value) => `${value}%`,
|
||||||
|
color: '#000',
|
||||||
|
font: { weight: 400, size: 12 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animation: { animateRotate: true }
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))
|
||||||
|
)}
|
||||||
|
{surveyStats.totalParticipants === 0 ? <></> : (
|
||||||
|
<div className={styles.exportButtonWrapper}>
|
||||||
|
<button
|
||||||
|
ref={exportButtonRef}
|
||||||
|
className={styles.exportButtonContainer}
|
||||||
|
onClick={() => handleExportToExcel(survey.id)}
|
||||||
|
>
|
||||||
|
Экспорт в excel
|
||||||
|
</button>
|
||||||
|
{showExportPopup && (
|
||||||
|
<div className={styles.exportPopup}>
|
||||||
|
Файл скачивается
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
)}
|
||||||
|
</div>
|
||||||
<button className={styles.exportButtonContainer} onClick={() => handleExportToExcel(survey.id)}>Экспорт в excel</button>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -26,9 +26,50 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.buttonContainer {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup {
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
|
color: #333;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
white-space: nowrap;
|
||||||
|
animation: fadeInOut 2s ease-in-out;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInOut {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50%) translateY(5px);
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50%) translateY(5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.copyButton {
|
.copyButton {
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
/*background-color: #4CAF50;*/
|
|
||||||
background-color: #3788D6;
|
background-color: #3788D6;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
@ -40,5 +81,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.copyButton:hover {
|
.copyButton:hover {
|
||||||
background-color: #45a049;
|
background-color: rgb(14, 122, 221);
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import React from 'react';
|
import React, {useRef, useState} from 'react';
|
||||||
import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx";
|
import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx";
|
||||||
import styles from "./SettingSurvey.module.css";
|
import styles from "./SettingSurvey.module.css";
|
||||||
import TimeEvent from "../TimeEvent/TimeEvent.tsx";
|
import TimeEvent from "../TimeEvent/TimeEvent.tsx";
|
||||||
import SaveButton from "../SaveButton/SaveButton.tsx";
|
|
||||||
import {ISurvey} from "../../api/SurveyApi.ts";
|
import {ISurvey} from "../../api/SurveyApi.ts";
|
||||||
import {useLocation, useOutletContext} from "react-router-dom";
|
import {useLocation, useOutletContext} from "react-router-dom";
|
||||||
import {useSurveyContext} from "../../context/SurveyContext.tsx";
|
import {useSurveyContext} from "../../context/SurveyContext.tsx";
|
||||||
|
|
@ -16,13 +15,17 @@ const SettingSurvey: React.FC = () => {
|
||||||
setSurvey: (survey: ISurvey) => void;
|
setSurvey: (survey: ISurvey) => void;
|
||||||
}>();
|
}>();
|
||||||
const { tempSurvey, setTempSurvey } = useSurveyContext();
|
const { tempSurvey, setTempSurvey } = useSurveyContext();
|
||||||
|
const [showPopup, setShowPopup] = useState(false);
|
||||||
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
const handleCopyLink = () => {
|
const handleCopyLink = () => {
|
||||||
if (!survey?.id)
|
if (!survey?.id) return;
|
||||||
return;
|
|
||||||
const link = `${window.location.origin}/complete-survey/${survey.id}`;
|
const link = `${window.location.origin}/complete-survey/${survey.id}`;
|
||||||
navigator.clipboard.writeText(link)
|
navigator.clipboard.writeText(link)
|
||||||
.then(() => console.log('Copied!'))
|
.then(() => {
|
||||||
|
setShowPopup(true);
|
||||||
|
setTimeout(() => setShowPopup(false), 2000);
|
||||||
|
})
|
||||||
.catch(error => console.error(`Не удалось скопировать ссылку: ${error}`));
|
.catch(error => console.error(`Не удалось скопировать ссылку: ${error}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,8 +53,22 @@ const SettingSurvey: React.FC = () => {
|
||||||
<div className={styles.param}>
|
<div className={styles.param}>
|
||||||
<h2>Параметры видимости</h2>
|
<h2>Параметры видимости</h2>
|
||||||
</div>
|
</div>
|
||||||
<SaveButton onClick={() => {}}/>
|
{!isSettingCreatePage && (
|
||||||
{!isSettingCreatePage ? <button onClick={handleCopyLink} className={styles.copyButton}>Копировать ссылку</button> : ''}
|
<div className={styles.buttonContainer}>
|
||||||
|
<button
|
||||||
|
ref={buttonRef}
|
||||||
|
onClick={handleCopyLink}
|
||||||
|
className={styles.copyButton}
|
||||||
|
>
|
||||||
|
Копировать ссылку
|
||||||
|
</button>
|
||||||
|
{showPopup && (
|
||||||
|
<div className={styles.popup}>
|
||||||
|
Ссылка скопирована
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -235,12 +235,13 @@ export const SurveyPage: React.FC = () => {
|
||||||
surveyId: id,
|
surveyId: id,
|
||||||
id: question.id,
|
id: question.id,
|
||||||
title: question.text,
|
title: question.text,
|
||||||
questionType: question.questionType // Убедитесь, что передается новый тип
|
questionType: question.questionType
|
||||||
} as QuestionActionData & { id: number }
|
} as QuestionActionData & { id: number }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
questions.forEach(question => {
|
questions.forEach(question => {
|
||||||
question.answerVariants.forEach(answer => {
|
question.answerVariants.forEach(answer => {
|
||||||
if (!answer.id) {
|
if (!answer.id) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue