add page results

This commit is contained in:
Tatiana Nikolaeva 2025-05-26 12:50:14 +05:00
parent 33f2b5ef62
commit 6b8197ce10
6 changed files with 396 additions and 8 deletions

View file

@ -1,5 +1,148 @@
/*Results.module.css*/
/* Results.module.css */
.results{
.results {
width: 85%;
}
margin: 19px 30px;
}
.statsContainer {
display: flex;
justify-content: space-between;
align-items: stretch;
margin: 30px 0;
gap: 17px;
}
.statItem {
display: flex;
flex-direction: column;
border-radius: 15px;
min-height: 180px;
padding: 20px;
box-sizing: border-box;
position: relative;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.countAnswer {
width: 36%;
background-color: #65B953;
}
.completion_percentage {
width: 36%;
background-color: #EEDD59;
}
.status {
width: 24%;
background-color: #A763EB;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.statItem h3 {
margin: 0 0 15px 0;
color: #FFFFFF;
font-size: 28px;
font-weight: 600;
line-height: 1.2;
}
.result {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-top: auto;
}
.statItem p {
padding: 20px;
font-weight: 600;
font-size: 40px;
color: #FFFFFF;
margin: 0;
line-height: 1;
}
.countAnswer p,
.completion_percentage p {
font-size: 60px;
}
.imgGroup,
.imgSend {
width: 58px;
height: 61px;
align-self: flex-end;
}
.status p {
text-align: center;
margin-top: auto;
font-size: 32px;
}
.questionContainer {
display: flex;
flex-direction: column;
margin-bottom: 40px;
padding: 25px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.questionContent {
display: flex;
flex-direction: row;
gap: 40px;
align-items: flex-start;
width: 100%;
}
.textContainer {
display: flex;
flex-direction: column;
gap: 11px;
width: 30%;
min-width: 250px;
}
.questionContainer h3 {
font-size: 24px;
font-weight: 600;
color: #000000;
margin: 0;
}
.answerCount {
color: #000000;
font-size: 18px;
font-weight: 600;
}
.chartContainer {
flex: 1;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.pieContainer {
width: 100%;
height: 450px;
position: relative;
}
.barContainer {
width: 100%;
height: 450px;
display: flex;
justify-content: center;
align-items: center;
padding-right: 150px;
}

View file

@ -1,16 +1,72 @@
import SurveyInfo from "../SurveyInfo/SurveyInfo.tsx";
import styles from './Results.module.css'
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} 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';
ChartJS.register(
ArcElement, Tooltip, Legend,
CategoryScale, LinearScale, BarElement, Title, ChartDataLabels, annotationPlugin
);
// Типы для данных
interface QuestionStats {
questionText: string;
totalAnswers: number;
options: {
text: string;
percentage: number;
}[];
isMultipleChoice?: boolean;
}
export const Results = () => {
const { survey, setSurvey } = useOutletContext<{
survey: ISurvey;
setSurvey: (survey: ISurvey) => void;
}>();
return(
const surveyStats = {
totalParticipants: 100,
completionPercentage: 80,
status: 'Активен',
questions: [
{
questionText: "Вопрос 1",
totalAnswers: 80,
options: [
{ text: "Вариант 1", percentage: 46 },
{ text: "Вариант 2", percentage: 15 },
{ text: "Вариант 3", percentage: 39 }
],
isMultipleChoice: false
},
{
questionText: "Вопрос 2",
totalAnswers: 100,
options: [
{ text: "Вариант 1", percentage: 50 },
{ text: "Вариант 2", percentage: 20 },
{ text: "Вариант 3", percentage: 100 },
{ text: "Вариант 4", percentage: 80 }
],
isMultipleChoice: true
}
] as QuestionStats[]
};
// Цветовая палитра
const colorsForPie = ['#67C587', '#C9EAD4', '#EAF6ED'];
const colorsForBar = ['#8979FF'];
return (
<div className={styles.results}>
<SurveyInfo
titleSurvey={survey.title}
@ -18,6 +74,135 @@ export const Results = () => {
setDescriptionSurvey={(value) => setSurvey({ ...survey, description: value })}
setTitleSurvey={(value) => setSurvey({ ...survey, title: value })}
/>
<div className={styles.statsContainer}>
<div className={`${styles.statItem} ${styles.countAnswer}`}>
<h3>Количество ответов</h3>
<div className={styles.result}>
<p>{surveyStats.totalParticipants}</p>
<Group className={styles.imgGroup}/>
</div>
</div>
<div className={`${styles.statItem} ${styles.completion_percentage}`}>
<h3>Процент завершения</h3>
<div className={styles.result}>
<p>{surveyStats.completionPercentage}%</p>
<Send className={styles.imgSend}/>
</div>
</div>
<div className={`${styles.statItem} ${styles.status}`}>
<h3>Статус опроса</h3>
<p>{surveyStats.status}</p>
</div>
</div>
{surveyStats.questions.map((question, index) => (
<div key={index} className={styles.questionContainer}>
<div className={styles.questionContent}>
<div className={styles.textContainer}>
<h3>{question.questionText}</h3>
<p className={styles.answerCount}>Ответов: {question.totalAnswers}</p>
</div>
<div className={styles.chartContainer}>
{question.isMultipleChoice ? (
<div className={styles.barContainer}>
<Bar
data={{
labels: question.options.map(opt => 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: 16, weight: 400 },
color: '#000'
}))
}
},
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: { callback: (val) => `${val}%` }
},
x: {
ticks: {
color: '#000000',
font: {
size: 16,
weight: 400
}
},
grid: { display: false }
}
}
}}
/>
</div>
) : (
<div className={styles.pieContainer}>
<Pie
data={{
labels: question.options.map(opt => 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: 18,
weight: 500
}
}
},
tooltip: {
callbacks: {
label: (ctx) => `${ctx.label}: ${ctx.raw}%`
}
},
datalabels: {
formatter: (value) => `${value}%`,
color: '#000',
font: { weight: 400, size: 16 }
}
},
animation: { animateRotate: true }
}}
/>
</div>
)}
</div>
</div>
</div>
))}
</div>
)
}
);
};