- PERMISSIONS.updateExpiryDate[row.original.type].includes(user.type)
+ PERMISSIONS.updateExpiryDate[row.original.type]?.includes(user.type)
? setSelectedUser(row.original)
: null
}
@@ -498,13 +497,13 @@ export default function UserList({
cell: ({ row, getValue }) => (
- PERMISSIONS.updateExpiryDate[row.original.type].includes(user.type)
+ PERMISSIONS.updateExpiryDate[row.original.type]?.includes(user.type)
? setSelectedUser(row.original)
: null
}
diff --git a/src/pages/(exam)/ExamPage.tsx b/src/pages/(exam)/ExamPage.tsx
index 52a81e5e..3e3a8a7c 100644
--- a/src/pages/(exam)/ExamPage.tsx
+++ b/src/pages/(exam)/ExamPage.tsx
@@ -357,7 +357,7 @@ export default function ExamPage({page}: Props) {
exercise,
solutions.find((x) => x.exercise === exercise.id)!,
evaluationID,
- index === 0 ? 1 : 2,
+ index + 1,
);
}),
)
diff --git a/src/pages/(generation)/LevelGeneration.tsx b/src/pages/(generation)/LevelGeneration.tsx
index 80ca0a94..fb692173 100644
--- a/src/pages/(generation)/LevelGeneration.tsx
+++ b/src/pages/(generation)/LevelGeneration.tsx
@@ -1,375 +1,298 @@
import Select from "@/components/Low/Select";
-import {
- Difficulty,
- LevelExam,
- MultipleChoiceExercise,
- MultipleChoiceQuestion,
- LevelPart,
-} from "@/interfaces/exam";
+import {Difficulty, LevelExam, MultipleChoiceExercise, MultipleChoiceQuestion, LevelPart} from "@/interfaces/exam";
import useExamStore from "@/stores/examStore";
-import { getExamById } from "@/utils/exams";
-import { playSound } from "@/utils/sound";
-import { Tab } from "@headlessui/react";
+import {getExamById} from "@/utils/exams";
+import {playSound} from "@/utils/sound";
+import {Tab} from "@headlessui/react";
import axios from "axios";
import clsx from "clsx";
-import { capitalize, sample } from "lodash";
-import { useRouter } from "next/router";
-import { useState } from "react";
-import { BsArrowRepeat, BsCheck, BsPencilSquare, BsX } from "react-icons/bs";
-import { toast } from "react-toastify";
-import { v4 } from "uuid";
+import {capitalize, sample} from "lodash";
+import {useRouter} from "next/router";
+import {useState} from "react";
+import {BsArrowRepeat, BsCheck, BsPencilSquare, BsX} from "react-icons/bs";
+import {toast} from "react-toastify";
+import {v4} from "uuid";
const DIFFICULTIES: Difficulty[] = ["easy", "medium", "hard"];
-const QuestionDisplay = ({
- question,
- onUpdate,
-}: {
- question: MultipleChoiceQuestion;
- onUpdate: (question: MultipleChoiceQuestion) => void;
-}) => {
- const [isEditing, setIsEditing] = useState(false);
- const [options, setOptions] = useState(question.options);
- const [answer, setAnswer] = useState(question.solution);
+const QuestionDisplay = ({question, onUpdate}: {question: MultipleChoiceQuestion; onUpdate: (question: MultipleChoiceQuestion) => void}) => {
+ const [isEditing, setIsEditing] = useState(false);
+ const [options, setOptions] = useState(question.options);
+ const [answer, setAnswer] = useState(question.solution);
- return (
-
-
- {question.id}. {question.prompt}{" "}
-
-
- {question.options.map((option, index) => (
-
- setAnswer(option.id)}
- >
- ({option.id})
- {" "}
- {isEditing ? (
-
- setOptions((prev) =>
- prev.map((x, idx) =>
- idx === index ? { ...x, text: e.target.value } : x
- )
- )
- }
- />
- ) : (
- {option.text}
- )}
-
- ))}
-
-
- {!isEditing && (
-
- )}
- {isEditing && (
- <>
-
-
- >
- )}
-
-
- );
+ return (
+
+
+ {question.id}. {question.prompt}{" "}
+
+
+ {question.options.map((option, index) => (
+
+ setAnswer(option.id)}>
+ ({option.id})
+ {" "}
+ {isEditing ? (
+ setOptions((prev) => prev.map((x, idx) => (idx === index ? {...x, text: e.target.value} : x)))}
+ />
+ ) : (
+ {option.text}
+ )}
+
+ ))}
+
+
+ {!isEditing && (
+
+ )}
+ {isEditing && (
+ <>
+
+
+ >
+ )}
+
+
+ );
};
-const TaskTab = ({
- exam,
- difficulty,
- setExam,
-}: {
- exam?: LevelPart;
- difficulty: Difficulty;
- setExam: (exam: LevelPart) => void;
-}) => {
- const [isLoading, setIsLoading] = useState(false);
+const TaskTab = ({exam, difficulty, setExam}: {exam?: LevelPart; difficulty: Difficulty; setExam: (exam: LevelPart) => void}) => {
+ const [isLoading, setIsLoading] = useState(false);
- const generate = () => {
- const url = new URLSearchParams();
- url.append("difficulty", difficulty);
+ const generate = () => {
+ const url = new URLSearchParams();
+ url.append("difficulty", difficulty);
- setIsLoading(true);
- axios
- .get(`/api/exam/level/generate/level?${url.toString()}`)
- .then((result) => {
- playSound(typeof result.data === "string" ? "error" : "check");
- if (typeof result.data === "string")
- return toast.error(
- "Something went wrong, please try to generate again."
- );
- setExam(result.data);
- })
- .catch((error) => {
- console.log(error);
- toast.error("Something went wrong!");
- })
- .finally(() => setIsLoading(false));
- };
+ setIsLoading(true);
+ axios
+ .get(`/api/exam/level/generate/level?${url.toString()}`)
+ .then((result) => {
+ playSound(typeof result.data === "string" ? "error" : "check");
+ if (typeof result.data === "string") return toast.error("Something went wrong, please try to generate again.");
+ setExam(result.data);
+ })
+ .catch((error) => {
+ console.log(error);
+ toast.error("Something went wrong!");
+ })
+ .finally(() => setIsLoading(false));
+ };
- const onUpdate = (question: MultipleChoiceQuestion) => {
- if (!exam) return;
+ const onUpdate = (question: MultipleChoiceQuestion) => {
+ if (!exam) return;
- const updatedExam = {
- ...exam,
- exercises: exam.exercises.map((x) => ({
- ...x,
- questions: (x as MultipleChoiceExercise).questions.map((q) =>
- q.id === question.id ? question : q
- ),
- }),
- ),
- };
- console.log(updatedExam);
- setExam(updatedExam as any);
- };
+ const updatedExam = {
+ ...exam,
+ exercises: exam.exercises.map((x) => ({
+ ...x,
+ questions: (x as MultipleChoiceExercise).questions.map((q) => (q.id === question.id ? question : q)),
+ })),
+ };
+ console.log(updatedExam);
+ setExam(updatedExam as any);
+ };
- return (
-
-
-
-
- {isLoading && (
-
-
-
- Generating...
-
-
- )}
- {exam && (
-
- {exam.exercises
- .filter((x) => x.type === "multipleChoice")
- .map((ex) => {
- const exercise = ex as MultipleChoiceExercise;
+ return (
+
+
+
+
+ {isLoading && (
+
+
+ Generating...
+
+ )}
+ {exam && (
+
+ {exam.exercises
+ .filter((x) => x.type === "multipleChoice")
+ .map((ex) => {
+ const exercise = ex as MultipleChoiceExercise;
- return (
-
-
-
- Multiple Choice
-
-
- {exercise.questions.length} questions
-
-
-
- {exercise.questions.map((question) => (
-
- ))}
-
-
- );
- })}
-
- )}
-
- );
+ return (
+
+
+ Multiple Choice
+
+ {exercise.questions.length} questions
+
+
+
+ {exercise.questions.map((question) => (
+
+ ))}
+
+
+ );
+ })}
+
+ )}
+
+ );
};
const LevelGeneration = () => {
- const [generatedExam, setGeneratedExam] = useState
();
- const [isLoading, setIsLoading] = useState(false);
- const [resultingExam, setResultingExam] = useState();
- const [difficulty, setDifficulty] = useState(
- sample(DIFFICULTIES)!
- );
+ const [generatedExam, setGeneratedExam] = useState();
+ const [isLoading, setIsLoading] = useState(false);
+ const [resultingExam, setResultingExam] = useState();
+ const [difficulty, setDifficulty] = useState(sample(DIFFICULTIES)!);
- const router = useRouter();
+ const router = useRouter();
- const setExams = useExamStore((state) => state.setExams);
- const setSelectedModules = useExamStore((state) => state.setSelectedModules);
+ const setExams = useExamStore((state) => state.setExams);
+ const setSelectedModules = useExamStore((state) => state.setSelectedModules);
- const loadExam = async (examId: string) => {
- const exam = await getExamById("level", examId.trim());
- if (!exam) {
- toast.error(
- "Unknown Exam ID! Please make sure you selected the right module and entered the right exam ID",
- {
- toastId: "invalid-exam-id",
- }
- );
+ const loadExam = async (examId: string) => {
+ const exam = await getExamById("level", examId.trim());
+ if (!exam) {
+ toast.error("Unknown Exam ID! Please make sure you selected the right module and entered the right exam ID", {
+ toastId: "invalid-exam-id",
+ });
- return;
- }
+ return;
+ }
- setExams([exam]);
- setSelectedModules(["level"]);
+ setExams([exam]);
+ setSelectedModules(["level"]);
- router.push("/exercises");
- };
+ router.push("/exercises");
+ };
- const submitExam = () => {
- if (!generatedExam) {
- toast.error("Please generate all tasks before submitting");
- return;
- }
+ const submitExam = () => {
+ if (!generatedExam) {
+ toast.error("Please generate all tasks before submitting");
+ return;
+ }
- setIsLoading(true);
+ setIsLoading(true);
- const exam: LevelExam = {
- isDiagnostic: false,
- minTimer: 25,
- module: "level",
- id: v4(),
- parts: [generatedExam],
- };
+ const exam: LevelExam = {
+ isDiagnostic: false,
+ minTimer: 25,
+ module: "level",
+ id: v4(),
+ parts: [generatedExam],
+ };
- axios
- .post(`/api/exam/level`, exam)
- .then((result) => {
- playSound("sent");
- console.log(`Generated Exam ID: ${result.data.id}`);
- toast.success(
- "This new exam has been generated successfully! Check the ID in our browser's console."
- );
- setResultingExam(result.data);
+ axios
+ .post(`/api/exam/level`, exam)
+ .then((result) => {
+ playSound("sent");
+ console.log(`Generated Exam ID: ${result.data.id}`);
+ toast.success("This new exam has been generated successfully! Check the ID in our browser's console.");
+ setResultingExam(result.data);
- setGeneratedExam(undefined);
- })
- .catch((error) => {
- console.log(error);
- toast.error(
- "Something went wrong while generating, please try again later."
- );
- })
- .finally(() => setIsLoading(false));
- };
+ setGeneratedExam(undefined);
+ })
+ .catch((error) => {
+ console.log(error);
+ toast.error("Something went wrong while generating, please try again later.");
+ })
+ .finally(() => setIsLoading(false));
+ };
- return (
- <>
-
-
-
-
-
-
-
-
- clsx(
- "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-ielts-level/70",
- "ring-white ring-opacity-60 ring-offset-2 ring-offset-ielts-level focus:outline-none focus:ring-2",
- "transition duration-300 ease-in-out",
- selected
- ? "bg-white shadow"
- : "text-blue-100 hover:bg-white/[0.12] hover:text-ielts-level"
- )
- }
- >
- Exam
-
-
-
-
-
-
-
- {resultingExam && (
-
- )}
-
-
- >
- );
+ return (
+ <>
+
+
+
+
+
+
+
+
+ clsx(
+ "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-ielts-level/70",
+ "ring-white ring-opacity-60 ring-offset-2 ring-offset-ielts-level focus:outline-none focus:ring-2",
+ "transition duration-300 ease-in-out",
+ selected ? "bg-white shadow" : "text-blue-100 hover:bg-white/[0.12] hover:text-ielts-level",
+ )
+ }>
+ Exam
+
+
+
+
+
+
+
+ {resultingExam && (
+
+ )}
+
+
+ >
+ );
};
export default LevelGeneration;
diff --git a/src/pages/record.tsx b/src/pages/record.tsx
index fb468dfb..1a934a6a 100644
--- a/src/pages/record.tsx
+++ b/src/pages/record.tsx
@@ -1,614 +1,517 @@
/* eslint-disable @next/next/no-img-element */
import Head from "next/head";
-import { withIronSessionSsr } from "iron-session/next";
-import { sessionOptions } from "@/lib/session";
-import { Stat, User } from "@/interfaces/user";
-import { useEffect, useState } from "react";
+import {withIronSessionSsr} from "iron-session/next";
+import {sessionOptions} from "@/lib/session";
+import {Stat, User} from "@/interfaces/user";
+import {useEffect, useState} from "react";
import useStats from "@/hooks/useStats";
-import { convertToUserSolutions, groupByDate } from "@/utils/stats";
+import {convertToUserSolutions, groupByDate} from "@/utils/stats";
import moment from "moment";
import useUsers from "@/hooks/useUsers";
import useExamStore from "@/stores/examStore";
-import { Module } from "@/interfaces";
-import { ToastContainer } from "react-toastify";
-import { useRouter } from "next/router";
-import { uniqBy } from "lodash";
-import { getExamById } from "@/utils/exams";
-import { sortByModule } from "@/utils/moduleUtils";
+import {Module} from "@/interfaces";
+import {ToastContainer} from "react-toastify";
+import {useRouter} from "next/router";
+import {uniqBy} from "lodash";
+import {getExamById} from "@/utils/exams";
+import {sortByModule} from "@/utils/moduleUtils";
import Layout from "@/components/High/Layout";
import clsx from "clsx";
-import { calculateBandScore } from "@/utils/score";
-import {
- BsBook,
- BsClipboard,
- BsClock,
- BsHeadphones,
- BsMegaphone,
- BsPen,
- BsPersonDash,
- BsPersonFillX,
- BsXCircle,
-} from "react-icons/bs";
+import {calculateBandScore} from "@/utils/score";
+import {BsBook, BsClipboard, BsClock, BsHeadphones, BsMegaphone, BsPen, BsPersonDash, BsPersonFillX, BsXCircle} from "react-icons/bs";
import Select from "@/components/Low/Select";
import useGroups from "@/hooks/useGroups";
-import { shouldRedirectHome } from "@/utils/navigation.disabled";
+import {shouldRedirectHome} from "@/utils/navigation.disabled";
import useAssignments from "@/hooks/useAssignments";
-import { uuidv4 } from "@firebase/util";
-import { usePDFDownload } from "@/hooks/usePDFDownload";
+import {uuidv4} from "@firebase/util";
+import {usePDFDownload} from "@/hooks/usePDFDownload";
import useRecordStore from "@/stores/recordStore";
-export const getServerSideProps = withIronSessionSsr(({ req, res }) => {
- const user = req.session.user;
+export const getServerSideProps = withIronSessionSsr(({req, res}) => {
+ const user = req.session.user;
- if (!user || !user.isVerified) {
- return {
- redirect: {
- destination: "/login",
- permanent: false,
- },
- };
- }
+ if (!user || !user.isVerified) {
+ return {
+ redirect: {
+ destination: "/login",
+ permanent: false,
+ },
+ };
+ }
- if (shouldRedirectHome(user)) {
- return {
- redirect: {
- destination: "/",
- permanent: false,
- },
- };
- }
+ if (shouldRedirectHome(user)) {
+ return {
+ redirect: {
+ destination: "/",
+ permanent: false,
+ },
+ };
+ }
- return {
- props: { user: req.session.user },
- };
+ return {
+ props: {user: req.session.user},
+ };
}, sessionOptions);
const defaultSelectableCorporate = {
- value: "",
- label: "All",
+ value: "",
+ label: "All",
};
-export default function History({ user }: { user: User }) {
- const [statsUserId, setStatsUserId] = useRecordStore((state) => [
- state.selectedUser,
- state.setSelectedUser,
- ]);
- // const [statsUserId, setStatsUserId] = useState(user.id);
- const [groupedStats, setGroupedStats] = useState<{ [key: string]: Stat[] }>();
- const [filter, setFilter] = useState<
- "months" | "weeks" | "days" | "assignments"
- >();
- const { assignments } = useAssignments({});
+export default function History({user}: {user: User}) {
+ const [statsUserId, setStatsUserId] = useRecordStore((state) => [state.selectedUser, state.setSelectedUser]);
+ // const [statsUserId, setStatsUserId] = useState(user.id);
+ const [groupedStats, setGroupedStats] = useState<{[key: string]: Stat[]}>();
+ const [filter, setFilter] = useState<"months" | "weeks" | "days" | "assignments">();
+ const {assignments} = useAssignments({});
- const { users } = useUsers();
- const { stats, isLoading: isStatsLoading } = useStats(statsUserId);
- const { groups: allGroups } = useGroups();
+ const {users} = useUsers();
+ const {stats, isLoading: isStatsLoading} = useStats(statsUserId);
+ const {groups: allGroups} = useGroups();
- const groups = allGroups.filter((x) => x.admin === user.id);
+ const groups = allGroups.filter((x) => x.admin === user.id);
- const setExams = useExamStore((state) => state.setExams);
- const setShowSolutions = useExamStore((state) => state.setShowSolutions);
- const setUserSolutions = useExamStore((state) => state.setUserSolutions);
- const setSelectedModules = useExamStore((state) => state.setSelectedModules);
- const setInactivity = useExamStore((state) => state.setInactivity);
- const setTimeSpent = useExamStore((state) => state.setTimeSpent);
- const router = useRouter();
- const renderPdfIcon = usePDFDownload("stats");
+ const setExams = useExamStore((state) => state.setExams);
+ const setShowSolutions = useExamStore((state) => state.setShowSolutions);
+ const setUserSolutions = useExamStore((state) => state.setUserSolutions);
+ const setSelectedModules = useExamStore((state) => state.setSelectedModules);
+ const setInactivity = useExamStore((state) => state.setInactivity);
+ const setTimeSpent = useExamStore((state) => state.setTimeSpent);
+ const router = useRouter();
+ const renderPdfIcon = usePDFDownload("stats");
- useEffect(() => {
- if (stats && !isStatsLoading) {
- setGroupedStats(
- groupByDate(
- stats.filter((x) => {
- if (
- (x.module === "writing" || x.module === "speaking") &&
- !x.isDisabled &&
- !x.solutions.every((y) => Object.keys(y).includes("evaluation"))
- )
- return false;
- return true;
- })
- )
- );
- }
- }, [stats, isStatsLoading]);
+ useEffect(() => {
+ if (stats && !isStatsLoading) {
+ setGroupedStats(
+ groupByDate(
+ stats.filter((x) => {
+ if (
+ (x.module === "writing" || x.module === "speaking") &&
+ !x.isDisabled &&
+ !x.solutions.every((y) => Object.keys(y).includes("evaluation"))
+ )
+ return false;
+ return true;
+ }),
+ ),
+ );
+ }
+ }, [stats, isStatsLoading]);
- // useEffect(() => {
- // // just set this initially
- // if (!statsUserId) setStatsUserId(user.id);
- // }, []);
+ // useEffect(() => {
+ // // just set this initially
+ // if (!statsUserId) setStatsUserId(user.id);
+ // }, []);
- const toggleFilter = (value: "months" | "weeks" | "days" | "assignments") => {
- setFilter((prev) => (prev === value ? undefined : value));
- };
+ const toggleFilter = (value: "months" | "weeks" | "days" | "assignments") => {
+ setFilter((prev) => (prev === value ? undefined : value));
+ };
- const filterStatsByDate = (stats: { [key: string]: Stat[] }) => {
- if (filter && filter !== "assignments") {
- const filterDate = moment()
- .subtract({ [filter as string]: 1 })
- .format("x");
- const filteredStats: { [key: string]: Stat[] } = {};
+ const filterStatsByDate = (stats: {[key: string]: Stat[]}) => {
+ if (filter && filter !== "assignments") {
+ const filterDate = moment()
+ .subtract({[filter as string]: 1})
+ .format("x");
+ const filteredStats: {[key: string]: Stat[]} = {};
- Object.keys(stats).forEach((timestamp) => {
- if (timestamp >= filterDate)
- filteredStats[timestamp] = stats[timestamp];
- });
+ Object.keys(stats).forEach((timestamp) => {
+ if (timestamp >= filterDate) filteredStats[timestamp] = stats[timestamp];
+ });
- return filteredStats;
- }
+ return filteredStats;
+ }
- if (filter && filter === "assignments") {
- const filteredStats: { [key: string]: Stat[] } = {};
+ if (filter && filter === "assignments") {
+ const filteredStats: {[key: string]: Stat[]} = {};
- Object.keys(stats).forEach((timestamp) => {
- if (
- stats[timestamp]
- .map((s) => s.assignment === undefined)
- .includes(false)
- )
- filteredStats[timestamp] = [
- ...stats[timestamp].filter((s) => !!s.assignment),
- ];
- });
+ Object.keys(stats).forEach((timestamp) => {
+ if (stats[timestamp].map((s) => s.assignment === undefined).includes(false))
+ filteredStats[timestamp] = [...stats[timestamp].filter((s) => !!s.assignment)];
+ });
- return filteredStats;
- }
+ return filteredStats;
+ }
- return stats;
- };
+ return stats;
+ };
- const formatTimestamp = (timestamp: string) => {
- const date = moment(parseInt(timestamp));
- const formatter = "YYYY/MM/DD - HH:mm";
+ const formatTimestamp = (timestamp: string) => {
+ const date = moment(parseInt(timestamp));
+ const formatter = "YYYY/MM/DD - HH:mm";
- return date.format(formatter);
- };
+ return date.format(formatter);
+ };
- const aggregateScoresByModule = (
- stats: Stat[]
- ): { module: Module; total: number; missing: number; correct: number }[] => {
- const scores: {
- [key in Module]: { total: number; missing: number; correct: number };
- } = {
- reading: {
- total: 0,
- correct: 0,
- missing: 0,
- },
- listening: {
- total: 0,
- correct: 0,
- missing: 0,
- },
- writing: {
- total: 0,
- correct: 0,
- missing: 0,
- },
- speaking: {
- total: 0,
- correct: 0,
- missing: 0,
- },
- level: {
- total: 0,
- correct: 0,
- missing: 0,
- },
- };
+ const aggregateScoresByModule = (stats: Stat[]): {module: Module; total: number; missing: number; correct: number}[] => {
+ const scores: {
+ [key in Module]: {total: number; missing: number; correct: number};
+ } = {
+ reading: {
+ total: 0,
+ correct: 0,
+ missing: 0,
+ },
+ listening: {
+ total: 0,
+ correct: 0,
+ missing: 0,
+ },
+ writing: {
+ total: 0,
+ correct: 0,
+ missing: 0,
+ },
+ speaking: {
+ total: 0,
+ correct: 0,
+ missing: 0,
+ },
+ level: {
+ total: 0,
+ correct: 0,
+ missing: 0,
+ },
+ };
- stats.forEach((x) => {
- scores[x.module!] = {
- total: scores[x.module!].total + x.score.total,
- correct: scores[x.module!].correct + x.score.correct,
- missing: scores[x.module!].missing + x.score.missing,
- };
- });
+ stats.forEach((x) => {
+ scores[x.module!] = {
+ total: scores[x.module!].total + x.score.total,
+ correct: scores[x.module!].correct + x.score.correct,
+ missing: scores[x.module!].missing + x.score.missing,
+ };
+ });
- return Object.keys(scores)
- .filter((x) => scores[x as Module].total > 0)
- .map((x) => ({ module: x as Module, ...scores[x as Module] }));
- };
+ return Object.keys(scores)
+ .filter((x) => scores[x as Module].total > 0)
+ .map((x) => ({module: x as Module, ...scores[x as Module]}));
+ };
- const customContent = (timestamp: string) => {
- if (!groupedStats) return <>>;
+ const customContent = (timestamp: string) => {
+ if (!groupedStats) return <>>;
- const dateStats = groupedStats[timestamp];
- const correct = dateStats.reduce(
- (accumulator, current) => accumulator + current.score.correct,
- 0
- );
- const total = dateStats.reduce(
- (accumulator, current) => accumulator + current.score.total,
- 0
- );
- const aggregatedScores = aggregateScoresByModule(dateStats).filter(
- (x) => x.total > 0
- );
- const assignmentID = dateStats.reduce(
- (_, current) => current.assignment as any,
- ""
- );
- const assignment = assignments.find((a) => a.id === assignmentID);
- const isDisabled = dateStats.some((x) => x.isDisabled);
+ const dateStats = groupedStats[timestamp];
+ const correct = dateStats.reduce((accumulator, current) => accumulator + current.score.correct, 0);
+ const total = dateStats.reduce((accumulator, current) => accumulator + current.score.total, 0);
+ const aggregatedScores = aggregateScoresByModule(dateStats).filter((x) => x.total > 0);
+ const assignmentID = dateStats.reduce((_, current) => current.assignment as any, "");
+ const assignment = assignments.find((a) => a.id === assignmentID);
+ const isDisabled = dateStats.some((x) => x.isDisabled);
- const aggregatedLevels = aggregatedScores.map((x) => ({
- module: x.module,
- level: calculateBandScore(x.correct, x.total, x.module, user.focus),
- }));
+ const aggregatedLevels = aggregatedScores.map((x) => ({
+ module: x.module,
+ level: calculateBandScore(x.correct, x.total, x.module, user.focus),
+ }));
- const { timeSpent, inactivity, session } = dateStats[0];
+ const {timeSpent, inactivity, session} = dateStats[0];
- const selectExam = () => {
- const examPromises = uniqBy(dateStats, "exam").map((stat) => {
- console.log({ stat });
- return getExamById(stat.module, stat.exam);
- });
+ const selectExam = () => {
+ const examPromises = uniqBy(dateStats, "exam").map((stat) => {
+ console.log({stat});
+ return getExamById(stat.module, stat.exam);
+ });
- Promise.all(examPromises).then((exams) => {
- if (exams.every((x) => !!x)) {
- if (!!timeSpent) setTimeSpent(timeSpent);
- if (!!inactivity) setInactivity(inactivity);
+ if (isDisabled) return;
- setUserSolutions(convertToUserSolutions(dateStats));
- setShowSolutions(true);
- setExams(exams.map((x) => x!).sort(sortByModule));
- setSelectedModules(
- exams
- .map((x) => x!)
- .sort(sortByModule)
- .map((x) => x!.module)
- );
- router.push("/exercises");
- }
- });
- };
+ Promise.all(examPromises).then((exams) => {
+ if (exams.every((x) => !!x)) {
+ if (!!timeSpent) setTimeSpent(timeSpent);
+ if (!!inactivity) setInactivity(inactivity);
- const textColor = clsx(
- correct / total >= 0.7 && "text-mti-purple",
- correct / total >= 0.3 && correct / total < 0.7 && "text-mti-red",
- correct / total < 0.3 && "text-mti-rose"
- );
+ setUserSolutions(convertToUserSolutions(dateStats));
+ setShowSolutions(true);
+ setExams(exams.map((x) => x!).sort(sortByModule));
+ setSelectedModules(
+ exams
+ .map((x) => x!)
+ .sort(sortByModule)
+ .map((x) => x!.module),
+ );
+ router.push("/exercises");
+ }
+ });
+ };
- const content = (
- <>
-
-
-
{formatTimestamp(timestamp)}
-
- {!!timeSpent && (
-
- {Math.floor(timeSpent / 60)} minutes
-
- )}
- {!!inactivity && (
-
- {Math.floor(inactivity / 60)} minutes
-
- )}
-
-
-
-
- Level{" "}
- {(
- aggregatedLevels.reduce(
- (accumulator, current) => accumulator + current.level,
- 0
- ) / aggregatedLevels.length
- ).toFixed(1)}
-
- {renderPdfIcon(session, textColor, textColor)}
-
-
+ const textColor = clsx(
+ correct / total >= 0.7 && "text-mti-purple",
+ correct / total >= 0.3 && correct / total < 0.7 && "text-mti-red",
+ correct / total < 0.3 && "text-mti-rose",
+ );
-
-
- {aggregatedLevels.map(({ module, level }) => (
-
- {module === "reading" && }
- {module === "listening" && }
- {module === "writing" && }
- {module === "speaking" && }
- {module === "level" && }
- {level.toFixed(1)}
-
- ))}
-
+ const content = (
+ <>
+
+
+
{formatTimestamp(timestamp)}
+
+ {!!timeSpent && (
+
+ {Math.floor(timeSpent / 60)} minutes
+
+ )}
+ {!!inactivity && (
+
+ {Math.floor(inactivity / 60)} minutes
+
+ )}
+
+
+
+
+ Level{" "}
+ {(aggregatedLevels.reduce((accumulator, current) => accumulator + current.level, 0) / aggregatedLevels.length).toFixed(1)}
+
+ {renderPdfIcon(session, textColor, textColor)}
+
+
- {assignment && (
-
- Assignment: {assignment.name}, Teacher:{" "}
- {users.find((u) => u.id === assignment.assigner)?.name}
-
- )}
-
- >
- );
+
+
+ {aggregatedLevels.map(({module, level}) => (
+
+ {module === "reading" && }
+ {module === "listening" && }
+ {module === "writing" && }
+ {module === "speaking" && }
+ {module === "level" && }
+ {level.toFixed(1)}
+
+ ))}
+
- return (
- <>
-
= 0.7 && "hover:border-mti-purple",
- correct / total >= 0.3 &&
- correct / total < 0.7 &&
- "hover:border-mti-red",
- correct / total < 0.3 && "hover:border-mti-rose"
- )}
- onClick={isDisabled ? () => null : selectExam}
- data-tip="This exam is still being evaluated..."
- role="button"
- >
- {content}
-
-
= 0.7 && "hover:border-mti-purple",
- correct / total >= 0.3 &&
- correct / total < 0.7 &&
- "hover:border-mti-red",
- correct / total < 0.3 && "hover:border-mti-rose"
- )}
- data-tip="Your screen size is too small to view previous exams."
- role="button"
- >
- {content}
-
- >
- );
- };
+ {assignment && (
+
+ Assignment: {assignment.name}, Teacher: {users.find((u) => u.id === assignment.assigner)?.name}
+
+ )}
+
+ >
+ );
- const selectableCorporates = [
- defaultSelectableCorporate,
- ...users
- .filter((x) => x.type === "corporate")
- .map((x) => ({
- value: x.id,
- label: `${x.name} - ${x.email}`,
- })),
- ];
+ return (
+ <>
+ = 0.7 && "hover:border-mti-purple",
+ correct / total >= 0.3 && correct / total < 0.7 && "hover:border-mti-red",
+ correct / total < 0.3 && "hover:border-mti-rose",
+ )}
+ onClick={selectExam}
+ data-tip="This exam is still being evaluated..."
+ role="button">
+ {content}
+
+ = 0.7 && "hover:border-mti-purple",
+ correct / total >= 0.3 && correct / total < 0.7 && "hover:border-mti-red",
+ correct / total < 0.3 && "hover:border-mti-rose",
+ )}
+ data-tip="Your screen size is too small to view previous exams."
+ role="button">
+ {content}
+
+ >
+ );
+ };
- const [selectedCorporate, setSelectedCorporate] = useState(
- defaultSelectableCorporate.value
- );
+ const selectableCorporates = [
+ defaultSelectableCorporate,
+ ...users
+ .filter((x) => x.type === "corporate")
+ .map((x) => ({
+ value: x.id,
+ label: `${x.name} - ${x.email}`,
+ })),
+ ];
- const getUsersList = (): User[] => {
- if (selectedCorporate) {
- // get groups for that corporate
- const selectedCorporateGroups = allGroups.filter(
- (x) => x.admin === selectedCorporate
- );
+ const [selectedCorporate, setSelectedCorporate] = useState(defaultSelectableCorporate.value);
- // get the teacher ids for that group
- const selectedCorporateGroupsParticipants =
- selectedCorporateGroups.flatMap((x) => x.participants);
+ const getUsersList = (): User[] => {
+ if (selectedCorporate) {
+ // get groups for that corporate
+ const selectedCorporateGroups = allGroups.filter((x) => x.admin === selectedCorporate);
- // // search for groups for these teachers
- // const teacherGroups = allGroups.filter((x) => {
- // return selectedCorporateGroupsParticipants.includes(x.admin);
- // });
+ // get the teacher ids for that group
+ const selectedCorporateGroupsParticipants = selectedCorporateGroups.flatMap((x) => x.participants);
- // const usersList = [
- // ...selectedCorporateGroupsParticipants,
- // ...teacherGroups.flatMap((x) => x.participants),
- // ];
- const userListWithUsers = selectedCorporateGroupsParticipants.map((x) =>
- users.find((y) => y.id === x)
- ) as User[];
- return userListWithUsers.filter((x) => x);
- }
+ // // search for groups for these teachers
+ // const teacherGroups = allGroups.filter((x) => {
+ // return selectedCorporateGroupsParticipants.includes(x.admin);
+ // });
- return users || [];
- };
+ // const usersList = [
+ // ...selectedCorporateGroupsParticipants,
+ // ...teacherGroups.flatMap((x) => x.participants),
+ // ];
+ const userListWithUsers = selectedCorporateGroupsParticipants.map((x) => users.find((y) => y.id === x)) as User[];
+ return userListWithUsers.filter((x) => x);
+ }
- const corporateFilteredUserList = getUsersList();
+ return users || [];
+ };
- const getSelectedUser = () => {
- if (selectedCorporate) {
- const userInCorporate = corporateFilteredUserList.find(
- (x) => x.id === statsUserId
- );
- return userInCorporate || corporateFilteredUserList[0];
- }
+ const corporateFilteredUserList = getUsersList();
- return users.find((x) => x.id === statsUserId) || user;
- };
+ const getSelectedUser = () => {
+ if (selectedCorporate) {
+ const userInCorporate = corporateFilteredUserList.find((x) => x.id === statsUserId);
+ return userInCorporate || corporateFilteredUserList[0];
+ }
- const selectedUser = getSelectedUser();
- const selectedUserSelectValue = selectedUser
- ? {
- value: selectedUser.id,
- label: `${selectedUser.name} - ${selectedUser.email}`,
- }
- : {
- value: "",
- label: "",
- };
- return (
- <>
-
- Record | EnCoach
-
-
-
-
-
- {user && (
-
-
-
- {(user.type === "developer" || user.type === "admin") && (
- <>
-
+ return users.find((x) => x.id === statsUserId) || user;
+ };
-
-
+ const selectedUser = getSelectedUser();
+ const selectedUserSelectValue = selectedUser
+ ? {
+ value: selectedUser.id,
+ label: `${selectedUser.name} - ${selectedUser.email}`,
+ }
+ : {
+ value: "",
+ label: "",
+ };
+ return (
+ <>
+
+
Record | EnCoach
+
+
+
+
+
+ {user && (
+
+
+
+ {(user.type === "developer" || user.type === "admin") && (
+ <>
+
-
-
-
-
-
-
-
-
- {groupedStats &&
- Object.keys(groupedStats).length > 0 &&
- !isStatsLoading && (
-
- {Object.keys(filterStatsByDate(groupedStats))
- .sort((a, b) => parseInt(b) - parseInt(a))
- .map(customContent)}
-
- )}
- {groupedStats &&
- Object.keys(groupedStats).length === 0 &&
- !isStatsLoading && (
-
- No record to display...
-
- )}
-
- )}
- >
- );
+
({
+ value: x.id,
+ label: `${x.name} - ${x.email}`,
+ }))}
+ value={selectedUserSelectValue}
+ onChange={(value) => setStatsUserId(value?.value)}
+ styles={{
+ menuPortal: (base) => ({...base, zIndex: 9999}),
+ option: (styles, state) => ({
+ ...styles,
+ backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white",
+ color: state.isFocused ? "black" : styles.color,
+ }),
+ }}
+ />
+ >
+ )}
+ {(user.type === "corporate" || user.type === "teacher") && groups.length > 0 && (
+ <>
+
+
+ groups.flatMap((y) => y.participants).includes(x.id))
+ .map((x) => ({
+ value: x.id,
+ label: `${x.name} - ${x.email}`,
+ }))}
+ value={selectedUserSelectValue}
+ onChange={(value) => setStatsUserId(value?.value)}
+ styles={{
+ menuPortal: (base) => ({...base, zIndex: 9999}),
+ option: (styles, state) => ({
+ ...styles,
+ backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white",
+ color: state.isFocused ? "black" : styles.color,
+ }),
+ }}
+ />
+ >
+ )}
+
+
+
+
+
+
+
+
+ {groupedStats && Object.keys(groupedStats).length > 0 && !isStatsLoading && (
+
+ {Object.keys(filterStatsByDate(groupedStats))
+ .sort((a, b) => parseInt(b) - parseInt(a))
+ .map(customContent)}
+
+ )}
+ {groupedStats && Object.keys(groupedStats).length === 0 && !isStatsLoading && (
+ No record to display...
+ )}
+
+ )}
+ >
+ );
}
diff --git a/src/utils/evaluation.ts b/src/utils/evaluation.ts
index c1e950b3..c363999e 100644
--- a/src/utils/evaluation.ts
+++ b/src/utils/evaluation.ts
@@ -51,7 +51,7 @@ export const evaluateSpeakingAnswer = async (
case "speaking":
return {...(await evaluateSpeakingExercise(exercise, exercise.id, solution, id, task)), id} as UserSolution;
case "interactiveSpeaking":
- return {...(await evaluateInteractiveSpeakingExercise(exercise.id, solution, id, exercise.variant)), id} as UserSolution;
+ return {...(await evaluateInteractiveSpeakingExercise(exercise.id, solution, id, task === 3 ? "final" : "initial")), id} as UserSolution;
default:
return undefined;
}