import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx"; import styles from './Results.module.css'; import {Bar, Pie} from 'react-chartjs-2'; import {Chart as ChartJS, ArcElement, Tooltip, Legend, CategoryScale, LinearScale, BarElement, Title} from 'chart.js'; import {useOutletContext} from "react-router-dom"; import {ISurvey, getSurveyById} from "../../api/SurveyApi.ts"; import ChartDataLabels from 'chartjs-plugin-datalabels'; import annotationPlugin from 'chartjs-plugin-annotation'; import Group from '../../assets/gmail_groups.svg?react'; import Send from '../../assets/send.svg?react'; import {getAllCompletions} from "../../api/CompletionApi.ts"; import {getAnswer} from "../../api/AnswerApi.ts"; import {useEffect, useState} from "react"; import {getListQuestions} from "../../api/QuestionApi.ts"; import {getAnswerVariants, IAnswerVariant} from "../../api/AnswerVariantsApi.ts"; import {getResultsFile} from "../../api/ExportResultApi.ts"; ChartJS.register( ArcElement, Tooltip, Legend, CategoryScale, LinearScale, BarElement, Title, ChartDataLabels, annotationPlugin ); interface QuestionStats { questionText: string; totalAnswers: number; options: { text: string; percentage: number; id: number; }[]; isMultipleChoice: boolean; } interface IAnswer { questionId: number; completionId: number; answerText: string; optionId?: number; } export const Results = () => { const { survey: initialSurvey, setSurvey } = useOutletContext<{ survey: ISurvey; setSurvey: (survey: ISurvey) => void; }>(); const [surveyStats, setSurveyStats] = useState({ totalParticipants: 0, completionPercentage: 0, status: 'Активен', questions: [] as QuestionStats[] }); const [survey, setLocalSurvey] = useState(initialSurvey); // const [questions, setQuestions] = useState([]); const handleExportToExcel = async (id: number) => { await getResultsFile(id) }; useEffect(() => { const fetchSurveyData = async () => { try { const surveyData = await getSurveyById(survey.id); setLocalSurvey(surveyData); const questionsList = await getListQuestions(survey.id); // setQuestions(questionsList); const questionsWithVariants = await Promise.all( questionsList.map(async (question) => { const variants = await getAnswerVariants(survey.id, question.id); return { ...question, options: variants, isMultipleChoice: question.questionType !== 'SingleAnswerQuestion' }; }) ); const completions = await getAllCompletions(survey.id); const totalParticipants = completions.length; const questionsWithAnswers = await Promise.all( questionsWithVariants.map(async (question) => { const answers = await getAnswer(question.id); return { ...question, answers: answers }; }) ); const questionsStats = questionsWithAnswers.map(question => { const questionAnswers = question.answers as IAnswer[]; let uniqueAnswers = questionAnswers; if (question.isMultipleChoice) { const groupedByCompletion = questionAnswers.reduce((acc, answer) => { if (!acc[answer.completionId]) { acc[answer.completionId] = []; } acc[answer.completionId].push(answer); return acc; }, {} as Record); uniqueAnswers = Object.values(groupedByCompletion).map(group => ({ ...group[0], answerText: group.map(a => a.answerText).join("; ") })); } const optionsStats = question.options.map((option: IAnswerVariant) => { const count = uniqueAnswers.filter(answer => question.isMultipleChoice ? answer.answerText.includes(option.text) : answer.answerText === option.text ).length; const percentage = totalParticipants > 0 ? Math.round((count / totalParticipants) * 100) : 0; return { text: option.text, percentage: percentage, id: option.id }; }); return { questionText: question.title, totalAnswers: uniqueAnswers.length, options: optionsStats, isMultipleChoice: question.isMultipleChoice }; }); const totalAnswersCount = questionsWithAnswers.reduce((sum, question) => { if (question.isMultipleChoice) { const uniqueCompletions = new Set( question.answers.map((answer : IAnswer) => answer.completionId) ); return sum + uniqueCompletions.size; } else { return sum + question.answers.length; } }, 0); const maxPossibleAnswers = questionsWithAnswers.length * totalParticipants; const completionPercentage = totalParticipants > 0 && maxPossibleAnswers > 0 ? Math.round((totalAnswersCount / maxPossibleAnswers) * 100) : 0; setSurveyStats({ totalParticipants, completionPercentage, status: 'Активен', questions: questionsStats }); } catch (error) { console.error('Error fetching survey data:', error); } }; fetchSurveyData(); }, [survey.id]); const colorsForPie = ['#67C587', '#C9EAD4', '#EAF6ED']; const colorsForBar = ['#8979FF']; return (
{ setSurvey({ ...survey, description: value }); setLocalSurvey({ ...survey, description: value }); }} setTitleSurvey={(value) => { setSurvey({ ...survey, title: value }); setLocalSurvey({ ...survey, title: value }); }} />

Количество ответов

{surveyStats.totalParticipants}

Процент завершения

{surveyStats.completionPercentage}%

Статус опроса

{surveyStats.status}

{surveyStats.questions.map((question, index) => (

{question.questionText}

Ответов: {question.totalAnswers}

{question.isMultipleChoice ? (
opt.text), datasets: [{ label: '% выбравших', data: question.options.map(opt => opt.percentage), backgroundColor: colorsForBar, borderColor: colorsForBar, borderWidth: 2, borderRadius: 8, borderSkipped: false, }] }} options={{ responsive: true, plugins: { legend: { display: false }, tooltip: { enabled: true }, datalabels: { display: false }, annotation: { annotations: question.options.map((opt, i) => ({ type: 'label', xValue: i, yValue: opt.percentage + 5, content: `${opt.percentage}%`, font: { size: 1, weight: 400 }, color: '#000' })) } }, scales: { y: { beginAtZero: true, max: 100, ticks: { callback: (val) => `${val}%` } }, x: { ticks: { color: '#000000', font: { size: 12, weight: 400 } }, grid: { display: false } } } }} />
) : (
opt.text), datasets: [{ data: question.options.map(opt => opt.percentage), backgroundColor: colorsForPie, borderColor: '#fff', 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 } }} />
)}
))}
); };