Solved merge conflicts

This commit is contained in:
Tiago Ribeiro
2024-11-26 15:33:12 +00:00
12 changed files with 2659 additions and 23241 deletions

20626
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -96,6 +96,7 @@
}, },
"devDependencies": { "devDependencies": {
"@simbathesailor/use-what-changed": "^2.0.0", "@simbathesailor/use-what-changed": "^2.0.0",
"@types/": "welldone-software/why-did-you-render",
"@types/blob-stream": "^0.1.33", "@types/blob-stream": "^0.1.33",
"@types/formidable": "^3.4.0", "@types/formidable": "^3.4.0",
"@types/howler": "^2.2.11", "@types/howler": "^2.2.11",
@@ -107,7 +108,6 @@
"@types/react-datepicker": "^4.15.1", "@types/react-datepicker": "^4.15.1",
"@types/uuid": "^9.0.1", "@types/uuid": "^9.0.1",
"@types/wavesurfer.js": "^6.0.6", "@types/wavesurfer.js": "^6.0.6",
"@welldone-software/why-did-you-render": "^8.0.3",
"@wixc3/react-board": "^2.2.0", "@wixc3/react-board": "^2.2.0",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"husky": "^8.0.3", "husky": "^8.0.3",

View File

@@ -1,5 +1,3 @@
//import "@/utils/wdyr";
import { FillBlanksExercise, FillBlanksMCOption } from "@/interfaces/exam"; import { FillBlanksExercise, FillBlanksMCOption } from "@/interfaces/exam";
import clsx from "clsx"; import clsx from "clsx";
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -99,7 +97,7 @@ const FillBlanks: React.FC<FillBlanksExercise & CommonProps> = ({
shuffleMaps, shuffleMaps,
isPractice isPractice
})); }));
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [id, answers, type, isPractice, shuffleMaps, calculateScore]); }, [id, answers, type, isPractice, shuffleMaps, calculateScore]);
const [openDropdownId, setOpenDropdownId] = useState<string | null>(null); const [openDropdownId, setOpenDropdownId] = useState<string | null>(null);

View File

@@ -2,9 +2,9 @@ import React from "react";
import { BsClock, BsXCircle } from "react-icons/bs"; import { BsClock, BsXCircle } from "react-icons/bs";
import clsx from "clsx"; import clsx from "clsx";
import { Stat, User } from "@/interfaces/user"; import { Stat, User } from "@/interfaces/user";
import { Module, Step } from "@/interfaces"; import { Grading, Module, Step } from "@/interfaces";
import ai_usage from "@/utils/ai.detection"; import ai_usage from "@/utils/ai.detection";
import { calculateBandScore } from "@/utils/score"; import { calculateBandScore, getGradingLabel } from "@/utils/score";
import moment from "moment"; import moment from "moment";
import { Assignment } from "@/interfaces/results"; import { Assignment } from "@/interfaces/results";
import { uuidv4 } from "@firebase/util"; import { uuidv4 } from "@firebase/util";
@@ -15,6 +15,7 @@ import { getExamById } from "@/utils/exams";
import { Exam, UserSolution } from "@/interfaces/exam"; import { Exam, UserSolution } from "@/interfaces/exam";
import ModuleBadge from "../ModuleBadge"; import ModuleBadge from "../ModuleBadge";
import useExamStore from "@/stores/exam"; import useExamStore from "@/stores/exam";
import { findBy } from "@/utils";
const formatTimestamp = (timestamp: string | number) => { const formatTimestamp = (timestamp: string | number) => {
const time = typeof timestamp === "string" ? parseInt(timestamp) : timestamp; const time = typeof timestamp === "string" ? parseInt(timestamp) : timestamp;
@@ -70,6 +71,7 @@ const aggregateScoresByModule = (stats: Stat[]): { module: Module; total: number
interface StatsGridItemProps { interface StatsGridItemProps {
width?: string | undefined; width?: string | undefined;
height?: string | undefined; height?: string | undefined;
gradingSystems: Grading[]
examNumber?: number | undefined; examNumber?: number | undefined;
stats: Stat[]; stats: Stat[];
timestamp: string | number; timestamp: string | number;
@@ -88,6 +90,7 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
timestamp, timestamp,
user, user,
assignments, assignments,
gradingSystems,
users, users,
training, training,
selectedTrainingExams, selectedTrainingExams,
@@ -182,9 +185,34 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
return true; return true;
}; };
const levelAverage = () =>
aggregatedLevels.reduce((accumulator, current) => accumulator + current.level, 0) / aggregatedLevels.length
const renderLevelScore = () => {
const defaultLevelScore = levelAverage().toFixed(1)
if (!stats.every(s => s.module === "level")) return defaultLevelScore
if (gradingSystems.length === 0) return defaultLevelScore
const score = {
correct: stats.reduce((acc, curr) => acc + curr.score.correct, 0),
total: stats.reduce((acc, curr) => acc + curr.score.total, 0)
}
const level: number = calculateBandScore(score.correct, score.total, "level", user.focus);
if (!!assignment) {
const gradingSystem = findBy(gradingSystems, 'entity', assignment.entity)
if (!gradingSystem) return defaultLevelScore
return getGradingLabel(level, gradingSystem.steps)
}
return getGradingLabel(level, gradingSystems[0].steps)
}
const content = ( const content = (
<> <>
<div className="w-full flex justify-between -md:items-center 2xl:items-center"> <div className="w-full flex justify-between">
<div className="flex flex-col md:gap-1 -md:gap-2 2xl:gap-2"> <div className="flex flex-col md:gap-1 -md:gap-2 2xl:gap-2">
<span className="font-medium">{formatTimestamp(timestamp)}</span> <span className="font-medium">{formatTimestamp(timestamp)}</span>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -202,12 +230,10 @@ const StatsGridItem: React.FC<StatsGridItemProps> = ({
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="flex flex-row gap-2"> <div className="flex flex-row gap-2">
{!!assignment && (assignment.released || assignment.released === undefined) && ( {((!!assignment && (assignment.released || assignment.released === undefined)) || !assignment) && (
<span className={textColor}> <span className={textColor}>
Level{" "} Level{' '}
{( {renderLevelScore()}
aggregatedLevels.reduce((accumulator, current) => accumulator + current.level, 0) / aggregatedLevels.length
).toFixed(1)}
</span> </span>
)} )}
{shouldRenderPDFIcon() && renderPdfIcon(session, textColor, textColor)} {shouldRenderPDFIcon() && renderPdfIcon(session, textColor, textColor)}

View File

@@ -18,10 +18,7 @@ import {
BsHeadphones, BsHeadphones,
BsMegaphone, BsMegaphone,
BsPen, BsPen,
BsShareFill,
} from "react-icons/bs"; } from "react-icons/bs";
import { LevelScore } from "@/constants/ielts";
import { getLevelScore } from "@/utils/score";
import { capitalize } from "lodash"; import { capitalize } from "lodash";
import Modal from "@/components/Modal"; import Modal from "@/components/Modal";
import { UserSolution } from "@/interfaces/exam"; import { UserSolution } from "@/interfaces/exam";
@@ -67,6 +64,7 @@ export default function Finish({ user, practiceScores, scores, modules, informat
const router = useRouter() const router = useRouter()
useEffect(() => setSelectedScore(scores.find((x) => x.module === selectedModule)!), [scores, selectedModule]); useEffect(() => setSelectedScore(scores.find((x) => x.module === selectedModule)!), [scores, selectedModule]);
useEffect(() => setSelectedPracticeScore(practiceScores.find((x) => x.module === selectedModule)!), [practiceScores, selectedModule]);
const moduleColors: { [key in Module]: { progress: string; inner: string } } = { const moduleColors: { [key in Module]: { progress: string; inner: string } } = {
reading: { reading: {
@@ -289,7 +287,7 @@ export default function Finish({ user, practiceScores, scores, modules, informat
<div className="bg-mti-green mt-1 h-3 min-h-[0.75rem] w-3 min-w-[0.75rem] rounded-full" /> <div className="bg-mti-green mt-1 h-3 min-h-[0.75rem] w-3 min-w-[0.75rem] rounded-full" />
<div className="flex flex-col"> <div className="flex flex-col">
<span className="text-mti-green"> <span className="text-mti-green">
{selectedPracticeScore.correct} / {selectedScore.total} {selectedPracticeScore.correct} / {selectedPracticeScore.total}
</span> </span>
<span className="text-lg whitespace-nowrap">Practice Questions</span> <span className="text-lg whitespace-nowrap">Practice Questions</span>
</div> </div>

View File

@@ -1,5 +1,4 @@
/* eslint-disable @next/next/no-img-element */ /* eslint-disable @next/next/no-img-element */
//import "@/utils/wdyr";
import { withIronSessionSsr } from "iron-session/next"; import { withIronSessionSsr } from "iron-session/next";
import { sessionOptions } from "@/lib/session"; import { sessionOptions } from "@/lib/session";
@@ -112,4 +111,4 @@ const Page: React.FC<Props> = ({ user, assignment, exams = [], destinationURL =
} }
//Page.whyDidYouRender = true; //Page.whyDidYouRender = true;
export default Page; export default Page;

View File

@@ -26,7 +26,7 @@ import { mapBy, redirect, serialize } from "@/utils";
import { getEntitiesWithRoles } from "@/utils/entities.be"; import { getEntitiesWithRoles } from "@/utils/entities.be";
import { checkAccess } from "@/utils/permissions"; import { checkAccess } from "@/utils/permissions";
import { getGroups, getGroupsByEntities } from "@/utils/groups.be"; import { getGroups, getGroupsByEntities } from "@/utils/groups.be";
import { getGradingSystemByEntity } from "@/utils/grading.be"; import { getGradingSystemByEntities, getGradingSystemByEntity } from "@/utils/grading.be";
import { Grading } from "@/interfaces"; import { Grading } from "@/interfaces";
import { EntityWithRoles } from "@/interfaces/entity"; import { EntityWithRoles } from "@/interfaces/entity";
import CardList from "@/components/High/CardList"; import CardList from "@/components/High/CardList";
@@ -43,9 +43,10 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const entities = await getEntitiesWithRoles(checkAccess(user, ["admin", "developer"]) ? undefined : entityIDs) const entities = await getEntitiesWithRoles(checkAccess(user, ["admin", "developer"]) ? undefined : entityIDs)
const users = await (checkAccess(user, ["admin", "developer"]) ? getUsers() : getEntitiesUsers(mapBy(entities, 'id'))) const users = await (checkAccess(user, ["admin", "developer"]) ? getUsers() : getEntitiesUsers(mapBy(entities, 'id')))
const assignments = await (checkAccess(user, ["admin", "developer"]) ? getAssignments() : getEntitiesAssignments(mapBy(entities, 'id'))) const assignments = await (checkAccess(user, ["admin", "developer"]) ? getAssignments() : getEntitiesAssignments(mapBy(entities, 'id')))
const gradingSystems = await getGradingSystemByEntities(mapBy(entities, 'id'))
return { return {
props: serialize({ user, users, assignments, entities }), props: serialize({ user, users, assignments, entities, gradingSystems }),
}; };
}, sessionOptions); }, sessionOptions);
@@ -56,11 +57,12 @@ interface Props {
users: User[]; users: User[];
assignments: Assignment[]; assignments: Assignment[];
entities: EntityWithRoles[] entities: EntityWithRoles[]
gradingSystems: Grading[]
} }
const MAX_TRAINING_EXAMS = 10; const MAX_TRAINING_EXAMS = 10;
export default function History({ user, users, assignments, entities }: Props) { export default function History({ user, users, assignments, entities, gradingSystems }: Props) {
const router = useRouter(); const router = useRouter();
const [statsUserId, setStatsUserId, training, setTraining] = useRecordStore((state) => [ const [statsUserId, setStatsUserId, training, setTraining] = useRecordStore((state) => [
state.selectedUser, state.selectedUser,
@@ -158,6 +160,7 @@ export default function History({ user, users, assignments, entities }: Props) {
<StatsGridItem <StatsGridItem
key={uuidv4()} key={uuidv4()}
stats={dateStats} stats={dateStats}
gradingSystems={gradingSystems}
timestamp={timestamp} timestamp={timestamp}
user={user} user={user}
assignments={assignments} assignments={assignments}

View File

@@ -1,50 +1,50 @@
import {useEffect, useState} from "react"; import { useEffect, useState } from "react";
import {useRouter} from "next/router"; import { useRouter } from "next/router";
import axios from "axios"; import axios from "axios";
import {Tab} from "@headlessui/react"; import { Tab } from "@headlessui/react";
import {AiOutlineFileSearch} from "react-icons/ai"; import { AiOutlineFileSearch } from "react-icons/ai";
import {MdOutlinePlaylistAddCheckCircle, MdOutlineSelfImprovement} from "react-icons/md"; import { MdOutlinePlaylistAddCheckCircle, MdOutlineSelfImprovement } from "react-icons/md";
import {BsChatLeftDots} from "react-icons/bs"; import { BsChatLeftDots } from "react-icons/bs";
import Button from "@/components/Low/Button"; import Button from "@/components/Low/Button";
import clsx from "clsx"; import clsx from "clsx";
import Exercise from "@/training/Exercise"; import Exercise from "@/training/Exercise";
import TrainingScore from "@/training/TrainingScore"; import TrainingScore from "@/training/TrainingScore";
import {ITrainingContent, ITrainingTip} from "@/training/TrainingInterfaces"; import { ITrainingContent, ITrainingTip } from "@/training/TrainingInterfaces";
import formatTip from "@/training/FormatTip"; import formatTip from "@/training/FormatTip";
import {Stat, User} from "@/interfaces/user"; import { Stat, User } from "@/interfaces/user";
import Head from "next/head"; import Head from "next/head";
import Layout from "@/components/High/Layout"; import Layout from "@/components/High/Layout";
import {ToastContainer} from "react-toastify"; import { ToastContainer } from "react-toastify";
import {withIronSessionSsr} from "iron-session/next"; import { withIronSessionSsr } from "iron-session/next";
import {shouldRedirectHome} from "@/utils/navigation.disabled"; import { shouldRedirectHome } from "@/utils/navigation.disabled";
import {sessionOptions} from "@/lib/session"; import { sessionOptions } from "@/lib/session";
import qs from "qs"; import qs from "qs";
import StatsGridItem from "@/components/Medium/StatGridItem"; import StatsGridItem from "@/components/Medium/StatGridItem";
import useExamStore from "@/stores/exam"; import useExamStore from "@/stores/exam";
import {usePDFDownload} from "@/hooks/usePDFDownload"; import { usePDFDownload } from "@/hooks/usePDFDownload";
import useAssignments from "@/hooks/useAssignments"; import useAssignments from "@/hooks/useAssignments";
import useUsers from "@/hooks/useUsers"; import useUsers from "@/hooks/useUsers";
import Dropdown from "@/components/Dropdown"; import Dropdown from "@/components/Dropdown";
import InfiniteCarousel from "@/components/InfiniteCarousel"; import InfiniteCarousel from "@/components/InfiniteCarousel";
import {LuExternalLink} from "react-icons/lu"; import { LuExternalLink } from "react-icons/lu";
import {uniqBy} from "lodash"; import { uniqBy } from "lodash";
import {getExamById} from "@/utils/exams"; import { getExamById } from "@/utils/exams";
import {sortByModule} from "@/utils/moduleUtils"; import { sortByModule } from "@/utils/moduleUtils";
import { requestUser } from "@/utils/api"; import { requestUser } from "@/utils/api";
import { redirect, serialize } from "@/utils"; import { redirect, serialize } from "@/utils";
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res) const user = await requestUser(req, res)
if (!user) return redirect("/login") if (!user) return redirect("/login")
if (shouldRedirectHome(user)) redirect("/") if (shouldRedirectHome(user)) redirect("/")
return { return {
props: serialize({user}), props: serialize({ user }),
}; };
}, sessionOptions); }, sessionOptions);
const TrainingContent: React.FC<{user: User}> = ({user}) => { const TrainingContent: React.FC<{ user: User }> = ({ user }) => {
const renderPdfIcon = usePDFDownload("stats"); const renderPdfIcon = usePDFDownload("stats");
const dispatch = useExamStore((s) => s.dispatch); const dispatch = useExamStore((s) => s.dispatch);
@@ -53,11 +53,11 @@ const TrainingContent: React.FC<{user: User}> = ({user}) => {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [trainingTips, setTrainingTips] = useState<ITrainingTip[]>([]); const [trainingTips, setTrainingTips] = useState<ITrainingTip[]>([]);
const [currentTipIndex, setCurrentTipIndex] = useState(0); const [currentTipIndex, setCurrentTipIndex] = useState(0);
const {assignments} = useAssignments({}); const { assignments } = useAssignments({});
const {users} = useUsers(); const { users } = useUsers();
const router = useRouter(); const router = useRouter();
const {id} = router.query; const { id } = router.query;
useEffect(() => { useEffect(() => {
const fetchTrainingContent = async () => { const fetchTrainingContent = async () => {
@@ -78,14 +78,14 @@ const TrainingContent: React.FC<{user: User}> = ({user}) => {
return statResponse.data; return statResponse.data;
}), }),
); );
return {...exam, stats}; return { ...exam, stats };
}), }),
), ),
}; };
const tips = await axios.get<ITrainingTip[]>("/api/training/walkthrough", { const tips = await axios.get<ITrainingTip[]>("/api/training/walkthrough", {
params: {ids: trainingContent.tip_ids}, params: { ids: trainingContent.tip_ids },
paramsSerializer: (params) => qs.stringify(params, {arrayFormat: "repeat"}), paramsSerializer: (params) => qs.stringify(params, { arrayFormat: "repeat" }),
}); });
const processedTips = tips.data.map(formatTip); const processedTips = tips.data.map(formatTip);
@@ -115,7 +115,7 @@ const TrainingContent: React.FC<{user: User}> = ({user}) => {
return getExamById(stat.module, stat.exam); return getExamById(stat.module, stat.exam);
}); });
const {timeSpent, inactivity} = stats[0]; const { timeSpent, inactivity } = stats[0];
Promise.all(examPromises).then((exams) => { Promise.all(examPromises).then((exams) => {
if (exams.every((x) => !!x)) { if (exams.every((x) => !!x)) {
@@ -172,6 +172,7 @@ const TrainingContent: React.FC<{user: User}> = ({user}) => {
{trainingContent.exams.map((exam, examIndex) => ( {trainingContent.exams.map((exam, examIndex) => (
<StatsGridItem <StatsGridItem
key={`exam-${examIndex}`} key={`exam-${examIndex}`}
gradingSystems={[]}
width="380px" width="380px"
height="150px" height="150px"
examNumber={examIndex + 1} examNumber={examIndex + 1}
@@ -241,7 +242,7 @@ const TrainingContent: React.FC<{user: User}> = ({user}) => {
{trainingContent.weak_areas.map((x, index) => ( {trainingContent.weak_areas.map((x, index) => (
<Tab <Tab
key={index} key={index}
className={({selected}) => className={({ selected }) =>
clsx( clsx(
"text-[#53B2F9] pb-2 border-b-2", "text-[#53B2F9] pb-2 border-b-2",
"focus:outline-none", "focus:outline-none",

View File

@@ -1,9 +1,9 @@
import {Stat} from "@/interfaces/user"; import { Stat } from "@/interfaces/user";
import {capitalize, groupBy} from "lodash"; import { capitalize, groupBy } from "lodash";
import {convertCamelCaseToReadable} from "@/utils/string"; import { convertCamelCaseToReadable } from "@/utils/string";
import {UserSolution} from "@/interfaces/exam"; import { UserSolution } from "@/interfaces/exam";
import {Module} from "@/interfaces"; import { Module } from "@/interfaces";
import {MODULES} from "@/constants/ielts"; import { MODULES } from "@/constants/ielts";
import moment from "moment"; import moment from "moment";
export const timestampToMoment = (stat: Stat): moment.Moment => { export const timestampToMoment = (stat: Stat): moment.Moment => {
@@ -16,15 +16,15 @@ export const totalExams = (stats: Stat[]): number => {
}; };
export const averageScore = (stats: Stat[]): number => { export const averageScore = (stats: Stat[]): number => {
const {correct, total} = stats.reduce( const { correct, total } = stats.reduce(
(acc, current) => ({correct: acc.correct + current.score.correct, total: acc.total + current.score.total}), (acc, current) => ({ correct: acc.correct + current.score.correct, total: acc.total + current.score.total }),
{correct: 0, total: 0}, { correct: 0, total: 0 },
); );
return parseFloat(((correct / total) * 100).toFixed(2)); return parseFloat(((correct / total) * 100).toFixed(2));
}; };
export const formatModuleTotalStats = (stats: Stat[]): {label: string; value: number}[] => { export const formatModuleTotalStats = (stats: Stat[]): { label: string; value: number }[] => {
const moduleSessions: {[key: string]: string[]} = {}; const moduleSessions: { [key: string]: string[] } = {};
stats.forEach((stat) => { stats.forEach((stat) => {
if (stat.module in moduleSessions) { if (stat.module in moduleSessions) {
@@ -43,7 +43,7 @@ export const formatModuleTotalStats = (stats: Stat[]): {label: string; value: nu
}; };
export const totalExamsByModule = (stats: Stat[], module: Module): number => { export const totalExamsByModule = (stats: Stat[], module: Module): number => {
const moduleSessions: {[key: string]: string[]} = {}; const moduleSessions: { [key: string]: string[] } = {};
stats.forEach((stat) => { stats.forEach((stat) => {
if (stat.module in moduleSessions) { if (stat.module in moduleSessions) {
@@ -58,8 +58,8 @@ export const totalExamsByModule = (stats: Stat[], module: Module): number => {
return moduleSessions[module]?.length || 0; return moduleSessions[module]?.length || 0;
}; };
export const calculateModuleAverageScoreStats = (stats: Stat[]): {module: Module; value: number}[] => { export const calculateModuleAverageScoreStats = (stats: Stat[]): { module: Module; value: number }[] => {
const moduleScores: {[key: string]: {correct: number; total: number}} = {}; const moduleScores: { [key: string]: { correct: number; total: number } } = {};
stats.forEach((stat) => { stats.forEach((stat) => {
if (stat.module in moduleScores) { if (stat.module in moduleScores) {
@@ -82,11 +82,11 @@ export const calculateModuleAverageScoreStats = (stats: Stat[]): {module: Module
}); });
}; };
export const formatModuleAverageScoreStats = (stats: Stat[]): {label: string; value: number}[] => { export const formatModuleAverageScoreStats = (stats: Stat[]): { label: string; value: number }[] => {
return calculateModuleAverageScoreStats(stats).map((x) => ({label: capitalize(x.module), value: x.value})); return calculateModuleAverageScoreStats(stats).map((x) => ({ label: capitalize(x.module), value: x.value }));
}; };
export const formatExerciseTotalStats = (stats: Stat[]): {label: string; value: number}[] => { export const formatExerciseTotalStats = (stats: Stat[]): { label: string; value: number }[] => {
const totalExercises = stats.map((stat) => ({ const totalExercises = stats.map((stat) => ({
label: convertCamelCaseToReadable(stat.type), label: convertCamelCaseToReadable(stat.type),
value: stats.filter((x) => x.type === stat.type).length, value: stats.filter((x) => x.type === stat.type).length,
@@ -95,8 +95,8 @@ export const formatExerciseTotalStats = (stats: Stat[]): {label: string; value:
return totalExercises.filter((ex, index) => totalExercises.findIndex((x) => x.label === ex.label) === index); return totalExercises.filter((ex, index) => totalExercises.findIndex((x) => x.label === ex.label) === index);
}; };
export const formatExerciseAverageScoreStats = (stats: Stat[]): {label: string; value: number}[] => { export const formatExerciseAverageScoreStats = (stats: Stat[]): { label: string; value: number }[] => {
const typeScores: {[key: string]: {correct: number; total: number}} = {}; const typeScores: { [key: string]: { correct: number; total: number } } = {};
stats.forEach((stat) => { stats.forEach((stat) => {
if (stat.type in typeScores) { if (stat.type in typeScores) {
@@ -110,7 +110,7 @@ export const formatExerciseAverageScoreStats = (stats: Stat[]): {label: string;
}); });
return Object.keys(typeScores).map((x) => { return Object.keys(typeScores).map((x) => {
const {correct, total} = typeScores[x as keyof typeof typeScores]; const { correct, total } = typeScores[x as keyof typeof typeScores];
return { return {
label: convertCamelCaseToReadable(x), label: convertCamelCaseToReadable(x),
@@ -138,6 +138,7 @@ export const convertToUserSolutions = (stats: Stat[]): UserSolution[] => {
solutions: stat.solutions, solutions: stat.solutions,
type: stat.type, type: stat.type,
module: stat.module, module: stat.module,
shuffleMaps: stat.shuffleMaps shuffleMaps: stat.shuffleMaps,
isPractice: stat.isPractice
})); }));
}; };

View File

@@ -1,13 +0,0 @@
/// <reference types="@welldone-software/why-did-you-render" />
import React from "react";
if (process.env.NODE_ENV === "development") {
const whyDidYouRender = require("@welldone-software/why-did-you-render");
whyDidYouRender(React, {
trackAllPureComponents: true,
trackHooks: true,
logOwnerReasons: true,
collapseGroups: true,
});
}

View File

@@ -1,37 +1,43 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": [ "lib": [
"dom", "dom",
"dom.iterable", "dom.iterable",
"esnext" "esnext"
], ],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noEmit": true, "noEmit": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve",
"incremental": true, "incremental": true,
"baseUrl": ".", "baseUrl": ".",
"downlevelIteration": true, "downlevelIteration": true,
"paths": { "paths": {
"@/*": ["./src/*"], "@/*": [
"@/training/*": ["./src/components/TrainingContent/*"], "./src/*"
"@/editor/*": ["./src/components/ExamEditor/*"] ],
} "@/training/*": [
}, "./src/components/TrainingContent/*"
"include": [ ],
"next-env.d.ts", "@/editor/*": [
"**/*.ts", "./src/components/ExamEditor/*"
"**/*.tsx" ]
], }
"exclude": [ },
"node_modules" "include": [
] "next-env.d.ts",
} "**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
}

5017
yarn.lock

File diff suppressed because it is too large Load Diff