mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Refactoriser la LiveResultTable en fonction de ses composants
Fixes #236
This commit is contained in:
parent
b66fbc09b2
commit
9c9c17cd0f
10 changed files with 509 additions and 227 deletions
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { StudentType } from 'src/Types/StudentType';
|
||||
import LiveResultsTable from 'src/components/LiveResults/LiveResultsTable';
|
||||
import LiveResultsTable from 'src/components/LiveResults/LiveResultsTable/LiveResultsTable';
|
||||
import { QuestionType } from 'src/Types/QuestionType';
|
||||
import { BaseQuestion, parse } from 'gift-pegjs';
|
||||
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { StudentType } from 'src/Types/StudentType';
|
||||
import LiveResultsTableBody from 'src/components/LiveResults/LiveResultsTable/TableComponents/LiveResultsTableBody';
|
||||
import { QuestionType } from 'src/Types/QuestionType';
|
||||
import { BaseQuestion, parse } from 'gift-pegjs';
|
||||
|
||||
|
||||
const mockGiftQuestions = parse(
|
||||
`::Sample Question 1:: Sample Question 1 {=Answer 1 ~Answer 2}
|
||||
|
||||
::Sample Question 2:: Sample Question 2 {T}`);
|
||||
|
||||
const mockQuestions: QuestionType[] = mockGiftQuestions.map((question, index) => {
|
||||
if (question.type !== "Category")
|
||||
question.id = (index + 1).toString();
|
||||
const newMockQuestion = question;
|
||||
return {question : newMockQuestion as BaseQuestion};
|
||||
});
|
||||
|
||||
const mockStudents: StudentType[] = [
|
||||
{ id: "1", name: 'Student 1', answers: [{ idQuestion: 1, answer: 'Answer 1', isCorrect: true }] },
|
||||
{ id: "2", name: 'Student 2', answers: [{ idQuestion: 2, answer: 'Answer 2', isCorrect: false }] },
|
||||
];
|
||||
|
||||
const mockGetStudentGrade = jest.fn((student: StudentType) => {
|
||||
const correctAnswers = student.answers.filter(answer => answer.isCorrect).length;
|
||||
return (correctAnswers / mockQuestions.length) * 100;
|
||||
});
|
||||
|
||||
describe('LiveResultsTableBody', () => {
|
||||
test('renders LiveResultsTableBody component', () => {
|
||||
render(
|
||||
<LiveResultsTableBody
|
||||
maxQuestions={2}
|
||||
students={mockStudents}
|
||||
showUsernames={true}
|
||||
showCorrectAnswers={false}
|
||||
getStudentGrade={mockGetStudentGrade}
|
||||
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Student 1')).toBeInTheDocument();
|
||||
expect(screen.getByText('Student 2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('displays correct and incorrect answers', () => {
|
||||
render(
|
||||
<LiveResultsTableBody
|
||||
maxQuestions={2}
|
||||
students={mockStudents}
|
||||
showUsernames={true}
|
||||
showCorrectAnswers={true}
|
||||
getStudentGrade={mockGetStudentGrade}
|
||||
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Answer 1')).toBeInTheDocument();
|
||||
expect(screen.getByText('Answer 2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('displays icons for correct and incorrect answers when showCorrectAnswers is false', () => {
|
||||
render(
|
||||
<LiveResultsTableBody
|
||||
maxQuestions={2}
|
||||
students={mockStudents}
|
||||
showUsernames={true}
|
||||
showCorrectAnswers={false}
|
||||
getStudentGrade={mockGetStudentGrade}
|
||||
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByLabelText('correct')).toBeInTheDocument();
|
||||
expect(screen.getByLabelText('incorrect')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('hides usernames when showUsernames is false', () => {
|
||||
render(
|
||||
<LiveResultsTableBody
|
||||
maxQuestions={2}
|
||||
students={mockStudents}
|
||||
showUsernames={false}
|
||||
showCorrectAnswers={true}
|
||||
getStudentGrade={mockGetStudentGrade}
|
||||
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getAllByText('******').length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { StudentType } from 'src/Types/StudentType';
|
||||
import LiveResultsTableFooter from 'src/components/LiveResults/LiveResultsTable/TableComponents/LiveResultTableFooter';
|
||||
|
||||
|
||||
const mockStudents: StudentType[] = [
|
||||
{ id: "1", name: 'Student 1', answers: [{ idQuestion: 1, answer: 'Answer 1', isCorrect: true }] },
|
||||
{ id: "2", name: 'Student 2', answers: [{ idQuestion: 2, answer: 'Answer 2', isCorrect: false }] },
|
||||
];
|
||||
|
||||
const mockGetStudentGrade = jest.fn((student: StudentType) => {
|
||||
const correctAnswers = student.answers.filter(answer => answer.isCorrect).length;
|
||||
return (correctAnswers / 2) * 100; // Assuming there are 2 questions
|
||||
});
|
||||
|
||||
describe('LiveResultsTableFooter', () => {
|
||||
test('renders LiveResultsTableFooter component', () => {
|
||||
render(
|
||||
<LiveResultsTableFooter
|
||||
maxQuestions={2}
|
||||
students={mockStudents}
|
||||
getStudentGrade={mockGetStudentGrade}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('% réussite')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('calculates and displays correct answers per question', () => {
|
||||
render(
|
||||
<LiveResultsTableFooter
|
||||
maxQuestions={2}
|
||||
students={mockStudents}
|
||||
getStudentGrade={mockGetStudentGrade}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('50 %')).toBeInTheDocument();
|
||||
expect(screen.getByText('0 %')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('calculates and displays class average', () => {
|
||||
render(
|
||||
<LiveResultsTableFooter
|
||||
maxQuestions={2}
|
||||
students={mockStudents}
|
||||
getStudentGrade={mockGetStudentGrade}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('50 %')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import LiveResultsTableHeader from 'src/components/LiveResults/LiveResultsTable/TableComponents/LiveResultsTableHeader';
|
||||
|
||||
|
||||
const mockShowSelectedQuestion = jest.fn();
|
||||
|
||||
describe('LiveResultsTableHeader', () => {
|
||||
test('renders LiveResultsTableHeader component', () => {
|
||||
render(
|
||||
<LiveResultsTableHeader
|
||||
maxQuestions={5}
|
||||
showSelectedQuestion={mockShowSelectedQuestion}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("Nom d'utilisateur")).toBeInTheDocument();
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
expect(screen.getByText(`Q${i}`)).toBeInTheDocument();
|
||||
}
|
||||
expect(screen.getByText('% réussite')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('calls showSelectedQuestion when a question header is clicked', () => {
|
||||
render(
|
||||
<LiveResultsTableHeader
|
||||
maxQuestions={5}
|
||||
showSelectedQuestion={mockShowSelectedQuestion}
|
||||
/>
|
||||
);
|
||||
|
||||
const questionHeader = screen.getByText('Q1');
|
||||
fireEvent.click(questionHeader);
|
||||
|
||||
expect(mockShowSelectedQuestion).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
test('renders the correct number of question headers', () => {
|
||||
render(
|
||||
<LiveResultsTableHeader
|
||||
maxQuestions={3}
|
||||
showSelectedQuestion={mockShowSelectedQuestion}
|
||||
/>
|
||||
);
|
||||
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
expect(screen.getByText(`Q${i}`)).toBeInTheDocument();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Socket } from 'socket.io-client';
|
||||
import { QuestionType } from '../../Types/QuestionType';
|
||||
|
||||
import './liveResult.css';
|
||||
import {
|
||||
FormControlLabel,
|
||||
|
|
@ -10,7 +9,7 @@ import {
|
|||
Switch,
|
||||
} from '@mui/material';
|
||||
import { StudentType } from '../../Types/StudentType';
|
||||
import LiveResultsTable from './LiveResultsTable';
|
||||
import LiveResultsTable from './LiveResultsTable/LiveResultsTable';
|
||||
|
||||
|
||||
interface LiveResultsProps {
|
||||
|
|
@ -21,12 +20,11 @@ interface LiveResultsProps {
|
|||
students: StudentType[]
|
||||
}
|
||||
|
||||
|
||||
const LiveResults: React.FC<LiveResultsProps> = ({ questions, showSelectedQuestion, students }) => {
|
||||
const [showUsernames, setShowUsernames] = useState<boolean>(false);
|
||||
const [showCorrectAnswers, setShowCorrectAnswers] = useState<boolean>(false);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="action-bar mb-1">
|
||||
|
|
@ -58,13 +56,13 @@ const LiveResults: React.FC<LiveResultsProps> = ({ questions, showSelectedQuesti
|
|||
</div>
|
||||
|
||||
<div className="table-container">
|
||||
<LiveResultsTable
|
||||
students={students}
|
||||
questions={questions}
|
||||
showCorrectAnswers={showCorrectAnswers}
|
||||
showSelectedQuestion={showSelectedQuestion}
|
||||
showUsernames={showUsernames}
|
||||
/>
|
||||
<LiveResultsTable
|
||||
students={students}
|
||||
questions={questions}
|
||||
showCorrectAnswers={showCorrectAnswers}
|
||||
showSelectedQuestion={showSelectedQuestion}
|
||||
showUsernames={showUsernames}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,215 +0,0 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { Paper, Table, TableBody, TableCell, TableContainer, TableFooter, TableHead, TableRow } from '@mui/material';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faCheck, faCircleXmark } from '@fortawesome/free-solid-svg-icons';
|
||||
import { StudentType } from 'src/Types/StudentType';
|
||||
import { QuestionType } from '../../Types/QuestionType';
|
||||
import { FormattedTextTemplate } from '../GiftTemplate/templates/TextTypeTemplate';
|
||||
|
||||
interface LiveResultsTableProps {
|
||||
students: StudentType[];
|
||||
questions: QuestionType[];
|
||||
showCorrectAnswers: boolean;
|
||||
showSelectedQuestion: (index: number) => void;
|
||||
showUsernames: boolean;
|
||||
}
|
||||
|
||||
const LiveResultsTable: React.FC<LiveResultsTableProps> = ({
|
||||
questions,
|
||||
students,
|
||||
showCorrectAnswers,
|
||||
showSelectedQuestion,
|
||||
showUsernames
|
||||
}) => {
|
||||
|
||||
const maxQuestions = questions.length;
|
||||
|
||||
const getStudentGrade = (student: StudentType): number => {
|
||||
if (student.answers.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uniqueQuestions = new Set();
|
||||
let correctAnswers = 0;
|
||||
|
||||
for (const answer of student.answers) {
|
||||
const { idQuestion, isCorrect } = answer;
|
||||
|
||||
if (!uniqueQuestions.has(idQuestion)) {
|
||||
uniqueQuestions.add(idQuestion);
|
||||
|
||||
if (isCorrect) {
|
||||
correctAnswers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (correctAnswers / questions.length) * 100;
|
||||
};
|
||||
|
||||
const classAverage: number = useMemo(() => {
|
||||
let classTotal = 0;
|
||||
|
||||
students.forEach((student) => {
|
||||
classTotal += getStudentGrade(student);
|
||||
});
|
||||
|
||||
return classTotal / students.length;
|
||||
}, [students]);
|
||||
|
||||
const getCorrectAnswersPerQuestion = (index: number): number => {
|
||||
return (
|
||||
(students.filter((student) =>
|
||||
student.answers.some(
|
||||
(answer) =>
|
||||
parseInt(answer.idQuestion.toString()) === index + 1 && answer.isCorrect
|
||||
)
|
||||
).length / students.length) * 100
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell className="sticky-column">
|
||||
<div className="text-base text-bold">Nom d'utilisateur</div>
|
||||
</TableCell>
|
||||
{Array.from({ length: maxQuestions }, (_, index) => (
|
||||
<TableCell
|
||||
key={index}
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)'
|
||||
}}
|
||||
onClick={() => showSelectedQuestion(index)}
|
||||
>
|
||||
<div className="text-base text-bold blue">{`Q${index + 1}`}</div>
|
||||
</TableCell>
|
||||
))}
|
||||
<TableCell
|
||||
className="sticky-header"
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)'
|
||||
}}
|
||||
>
|
||||
<div className="text-base text-bold">% réussite</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{students.map((student) => (
|
||||
<TableRow key={student.id}>
|
||||
<TableCell
|
||||
className="sticky-column"
|
||||
sx={{
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)'
|
||||
}}
|
||||
>
|
||||
<div className="text-base">
|
||||
{showUsernames ? student.name : '******'}
|
||||
</div>
|
||||
</TableCell>
|
||||
{Array.from({ length: maxQuestions }, (_, index) => {
|
||||
const answer = student.answers.find(
|
||||
(answer) => parseInt(answer.idQuestion.toString()) === index + 1
|
||||
);
|
||||
const answerText = answer ? answer.answer.toString() : '';
|
||||
const isCorrect = answer ? answer.isCorrect : false;
|
||||
|
||||
return (
|
||||
<TableCell
|
||||
key={index}
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)'
|
||||
}}
|
||||
className={
|
||||
answerText === ''
|
||||
? ''
|
||||
: isCorrect
|
||||
? 'correct-answer'
|
||||
: 'incorrect-answer'
|
||||
}
|
||||
>
|
||||
{showCorrectAnswers ? (
|
||||
<div dangerouslySetInnerHTML={{ __html:FormattedTextTemplate({ format: '', text: answerText }) }}></div>
|
||||
) : isCorrect ? (
|
||||
<FontAwesomeIcon icon={faCheck} />
|
||||
) : (
|
||||
answerText !== '' && (
|
||||
<FontAwesomeIcon icon={faCircleXmark} />
|
||||
)
|
||||
)}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
<TableCell
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)',
|
||||
fontWeight: 'bold',
|
||||
color: 'rgba(0, 0, 0)'
|
||||
}}
|
||||
>
|
||||
{getStudentGrade(student).toFixed()} %
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow sx={{ backgroundColor: '#d3d3d34f' }}>
|
||||
<TableCell className="sticky-column" sx={{ color: 'black' }}>
|
||||
<div className="text-base text-bold">% réussite</div>
|
||||
</TableCell>
|
||||
{Array.from({ length: maxQuestions }, (_, index) => (
|
||||
<TableCell
|
||||
key={index}
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)',
|
||||
fontWeight: 'bold',
|
||||
color: 'rgba(0, 0, 0)'
|
||||
}}
|
||||
>
|
||||
{students.length > 0
|
||||
? `${getCorrectAnswersPerQuestion(index).toFixed()} %`
|
||||
: '-'}
|
||||
</TableCell>
|
||||
))}
|
||||
<TableCell
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)',
|
||||
fontWeight: 'bold',
|
||||
fontSize: '1rem',
|
||||
color: 'rgba(0, 0, 0)'
|
||||
}}
|
||||
>
|
||||
{students.length > 0 ? `${classAverage.toFixed()} %` : '-'}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LiveResultsTable;
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react';
|
||||
import { Paper, Table, TableContainer } from '@mui/material';
|
||||
import { StudentType } from 'src/Types/StudentType';
|
||||
import { QuestionType } from '../../../Types/QuestionType';
|
||||
import LiveResultsTableFooter from './TableComponents/LiveResultTableFooter';
|
||||
import LiveResultsTableHeader from './TableComponents/LiveResultsTableHeader';
|
||||
import LiveResultsTableBody from './TableComponents/LiveResultsTableBody';
|
||||
|
||||
interface LiveResultsTableProps {
|
||||
students: StudentType[];
|
||||
questions: QuestionType[];
|
||||
showCorrectAnswers: boolean;
|
||||
showSelectedQuestion: (index: number) => void;
|
||||
showUsernames: boolean;
|
||||
}
|
||||
|
||||
const LiveResultsTable: React.FC<LiveResultsTableProps> = ({
|
||||
questions,
|
||||
students,
|
||||
showSelectedQuestion,
|
||||
showUsernames,
|
||||
showCorrectAnswers
|
||||
}) => {
|
||||
|
||||
const maxQuestions = questions.length;
|
||||
|
||||
const getStudentGrade = (student: StudentType): number => {
|
||||
if (student.answers.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uniqueQuestions = new Set();
|
||||
let correctAnswers = 0;
|
||||
|
||||
for (const answer of student.answers) {
|
||||
const { idQuestion, isCorrect } = answer;
|
||||
|
||||
if (!uniqueQuestions.has(idQuestion)) {
|
||||
uniqueQuestions.add(idQuestion);
|
||||
|
||||
if (isCorrect) {
|
||||
correctAnswers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (correctAnswers / questions.length) * 100;
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
<Table size="small">
|
||||
<LiveResultsTableHeader
|
||||
maxQuestions={maxQuestions}
|
||||
showSelectedQuestion={showSelectedQuestion}
|
||||
/>
|
||||
<LiveResultsTableBody
|
||||
maxQuestions={maxQuestions}
|
||||
students={students}
|
||||
showUsernames={showUsernames}
|
||||
showCorrectAnswers={showCorrectAnswers}
|
||||
getStudentGrade={getStudentGrade}
|
||||
/>
|
||||
<LiveResultsTableFooter
|
||||
students={students}
|
||||
maxQuestions={maxQuestions}
|
||||
getStudentGrade={getStudentGrade}
|
||||
/>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default LiveResultsTable;
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
import { TableCell, TableFooter, TableRow } from "@mui/material";
|
||||
import React, { useMemo } from "react";
|
||||
import { StudentType } from "src/Types/StudentType";
|
||||
|
||||
interface LiveResultsFooterProps {
|
||||
students: StudentType[];
|
||||
maxQuestions: number;
|
||||
getStudentGrade: (student: StudentType) => number;
|
||||
}
|
||||
|
||||
const LiveResultsTableFooter: React.FC<LiveResultsFooterProps> = ({
|
||||
maxQuestions,
|
||||
students,
|
||||
getStudentGrade
|
||||
|
||||
}) => {
|
||||
|
||||
const getCorrectAnswersPerQuestion = (index: number): number => {
|
||||
return (
|
||||
(students.filter((student) =>
|
||||
student.answers.some(
|
||||
(answer) =>
|
||||
parseInt(answer.idQuestion.toString()) === index + 1 && answer.isCorrect
|
||||
)
|
||||
).length / students.length) * 100
|
||||
);
|
||||
};
|
||||
|
||||
const classAverage: number = useMemo(() => {
|
||||
let classTotal = 0;
|
||||
|
||||
students.forEach((student) => {
|
||||
classTotal += getStudentGrade(student);
|
||||
});
|
||||
|
||||
return classTotal / students.length;
|
||||
}, [students]);
|
||||
|
||||
return (
|
||||
<TableFooter>
|
||||
<TableRow sx={{ backgroundColor: '#d3d3d34f' }}>
|
||||
<TableCell className="sticky-column" sx={{ color: 'black' }}>
|
||||
<div className="text-base text-bold">% réussite</div>
|
||||
</TableCell>
|
||||
{Array.from({ length: maxQuestions }, (_, index) => (
|
||||
<TableCell
|
||||
key={index}
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)',
|
||||
fontWeight: 'bold',
|
||||
color: 'rgba(0, 0, 0)'
|
||||
}}
|
||||
>
|
||||
{students.length > 0
|
||||
? `${getCorrectAnswersPerQuestion(index).toFixed()} %`
|
||||
: '-'}
|
||||
</TableCell>
|
||||
))}
|
||||
<TableCell
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)',
|
||||
fontWeight: 'bold',
|
||||
fontSize: '1rem',
|
||||
color: 'rgba(0, 0, 0)'
|
||||
}}
|
||||
>
|
||||
{students.length > 0 ? `${classAverage.toFixed()} %` : '-'}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
);
|
||||
};
|
||||
export default LiveResultsTableFooter;
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { TableBody, TableCell, TableRow } from "@mui/material";
|
||||
import { faCheck, faCircleXmark } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FormattedTextTemplate } from '../../../GiftTemplate/templates/TextTypeTemplate';
|
||||
import React from "react";
|
||||
import { StudentType } from "src/Types/StudentType";
|
||||
|
||||
interface LiveResultsFooterProps {
|
||||
maxQuestions: number;
|
||||
students: StudentType[];
|
||||
showUsernames: boolean;
|
||||
showCorrectAnswers: boolean;
|
||||
getStudentGrade: (student: StudentType) => number;
|
||||
|
||||
}
|
||||
|
||||
const LiveResultsTableFooter: React.FC<LiveResultsFooterProps> = ({
|
||||
maxQuestions,
|
||||
students,
|
||||
showUsernames,
|
||||
showCorrectAnswers,
|
||||
getStudentGrade
|
||||
}) => {
|
||||
|
||||
return (
|
||||
<TableBody>
|
||||
{students.map((student) => (
|
||||
<TableRow key={student.id}>
|
||||
<TableCell
|
||||
className="sticky-column"
|
||||
sx={{
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)'
|
||||
}}
|
||||
>
|
||||
<div className="text-base">
|
||||
{showUsernames ? student.name : '******'}
|
||||
</div>
|
||||
</TableCell>
|
||||
{Array.from({ length: maxQuestions }, (_, index) => {
|
||||
const answer = student.answers.find(
|
||||
(answer) => parseInt(answer.idQuestion.toString()) === index + 1
|
||||
);
|
||||
const answerText = answer ? answer.answer.toString() : '';
|
||||
const isCorrect = answer ? answer.isCorrect : false;
|
||||
|
||||
return (
|
||||
<TableCell
|
||||
key={index}
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)'
|
||||
}}
|
||||
className={
|
||||
answerText === ''
|
||||
? ''
|
||||
: isCorrect
|
||||
? 'correct-answer'
|
||||
: 'incorrect-answer'
|
||||
}
|
||||
>
|
||||
{showCorrectAnswers ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: FormattedTextTemplate({ format: '', text: answerText }) }}></div>
|
||||
) : isCorrect ? (
|
||||
<FontAwesomeIcon icon={faCheck} aria-label="correct" />
|
||||
) : (
|
||||
answerText !== '' && (
|
||||
<FontAwesomeIcon icon={faCircleXmark} aria-label="incorrect"/>
|
||||
)
|
||||
)}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
<TableCell
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)',
|
||||
fontWeight: 'bold',
|
||||
color: 'rgba(0, 0, 0)'
|
||||
}}
|
||||
>
|
||||
{getStudentGrade(student).toFixed()} %
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
);
|
||||
};
|
||||
export default LiveResultsTableFooter;
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import { TableCell, TableHead, TableRow } from "@mui/material";
|
||||
import React from "react";
|
||||
|
||||
interface LiveResultsFooterProps {
|
||||
maxQuestions: number;
|
||||
showSelectedQuestion: (index: number) => void;
|
||||
}
|
||||
|
||||
const LiveResultsTableFooter: React.FC<LiveResultsFooterProps> = ({
|
||||
maxQuestions,
|
||||
showSelectedQuestion,
|
||||
}) => {
|
||||
|
||||
return (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell className="sticky-column">
|
||||
<div className="text-base text-bold">Nom d'utilisateur</div>
|
||||
</TableCell>
|
||||
{Array.from({ length: maxQuestions }, (_, index) => (
|
||||
<TableCell
|
||||
key={index}
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)'
|
||||
}}
|
||||
onClick={() => showSelectedQuestion(index)}
|
||||
>
|
||||
<div className="text-base text-bold blue">{`Q${index + 1}`}</div>
|
||||
</TableCell>
|
||||
))}
|
||||
<TableCell
|
||||
className="sticky-header"
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(224, 224, 224, 1)'
|
||||
}}
|
||||
>
|
||||
<div className="text-base text-bold">% réussite</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
);
|
||||
};
|
||||
export default LiveResultsTableFooter;
|
||||
Loading…
Reference in a new issue