Updated the stats and record page
This commit is contained in:
@@ -19,6 +19,8 @@ import Layout from "@/components/High/Layout";
|
|||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {calculateBandScore} from "@/utils/score";
|
import {calculateBandScore} from "@/utils/score";
|
||||||
import {BsBook, BsHeadphones, BsMegaphone, BsPen} from "react-icons/bs";
|
import {BsBook, BsHeadphones, BsMegaphone, BsPen} from "react-icons/bs";
|
||||||
|
import Select from "react-select";
|
||||||
|
import useGroups from "@/hooks/useGroups";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
||||||
const user = req.session.user;
|
const user = req.session.user;
|
||||||
@@ -40,12 +42,13 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
export default function History({user}: {user: User}) {
|
export default function History({user}: {user: User}) {
|
||||||
const [selectedUser, setSelectedUser] = useState<User>(user);
|
const [statsUserId, setStatsUserId] = useState<string | undefined>(user.id);
|
||||||
const [groupedStats, setGroupedStats] = useState<{[key: string]: Stat[]}>();
|
const [groupedStats, setGroupedStats] = useState<{[key: string]: Stat[]}>();
|
||||||
const [filter, setFilter] = useState<"months" | "weeks" | "days">();
|
const [filter, setFilter] = useState<"months" | "weeks" | "days">();
|
||||||
|
|
||||||
const {users, isLoading: isUsersLoading} = useUsers();
|
const {users} = useUsers();
|
||||||
const {stats, isLoading: isStatsLoading} = useStats(selectedUser?.id);
|
const {stats, isLoading: isStatsLoading} = useStats(statsUserId);
|
||||||
|
const {groups} = useGroups(user.id);
|
||||||
|
|
||||||
const setExams = useExamStore((state) => state.setExams);
|
const setExams = useExamStore((state) => state.setExams);
|
||||||
const setShowSolutions = useExamStore((state) => state.setShowSolutions);
|
const setShowSolutions = useExamStore((state) => state.setShowSolutions);
|
||||||
@@ -218,22 +221,25 @@ export default function History({user}: {user: User}) {
|
|||||||
{user && (
|
{user && (
|
||||||
<Layout user={user}>
|
<Layout user={user}>
|
||||||
<div className="w-full flex justify-between items-center">
|
<div className="w-full flex justify-between items-center">
|
||||||
<div className="w-fit">
|
<div className="w-3/4">
|
||||||
{!isUsersLoading && user.type !== "student" && (
|
{(user.type === "developer" || user.type === "owner") && (
|
||||||
<>
|
<Select
|
||||||
<select
|
options={users.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))}
|
||||||
className="select w-full max-w-xs bg-white border border-mti-gray-platinum outline-none font-normal text-base"
|
defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}}
|
||||||
onChange={(e) => setSelectedUser(users.find((x) => x.id === e.target.value)!)}>
|
onChange={(value) => setStatsUserId(value?.value)}
|
||||||
{users.map((x) => (
|
/>
|
||||||
<option key={x.id} selected={selectedUser.id === x.id} value={x.id}>
|
)}
|
||||||
{x.name}
|
{(user.type === "admin" || user.type === "teacher") && groups.length > 0 && (
|
||||||
</option>
|
<Select
|
||||||
))}
|
options={users
|
||||||
</select>
|
.filter((x) => groups.flatMap((y) => y.participants).includes(x.id))
|
||||||
</>
|
.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))}
|
||||||
|
defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}}
|
||||||
|
onChange={(value) => setStatsUserId(value?.value)}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4 w-full justify-end">
|
||||||
<button
|
<button
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light",
|
"bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light",
|
||||||
|
|||||||
@@ -1,24 +1,13 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Navbar from "@/components/Navbar";
|
import {BsFileEarmarkText, BsPencil, BsStar} from "react-icons/bs";
|
||||||
import {BsFileEarmarkText, BsPencil, BsStar, BsBook, BsHeadphones, BsPen, BsMegaphone} from "react-icons/bs";
|
import {LinearScale, Chart as ChartJS, CategoryScale, PointElement, LineElement, Legend, Tooltip, LineController} from "chart.js";
|
||||||
import {ArcElement, LinearScale, Chart as ChartJS, CategoryScale, PointElement, LineElement, Legend, Tooltip} from "chart.js";
|
|
||||||
import {withIronSessionSsr} from "iron-session/next";
|
import {withIronSessionSsr} from "iron-session/next";
|
||||||
import {sessionOptions} from "@/lib/session";
|
import {sessionOptions} from "@/lib/session";
|
||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import useStats from "@/hooks/useStats";
|
import useStats from "@/hooks/useStats";
|
||||||
import {
|
import {averageScore, totalExamsByModule, groupBySession, groupByModule} from "@/utils/stats";
|
||||||
averageScore,
|
|
||||||
totalExams,
|
|
||||||
totalExamsByModule,
|
|
||||||
groupBySession,
|
|
||||||
groupByModule,
|
|
||||||
formatModuleAverageScoreStats,
|
|
||||||
calculateModuleAverageScoreStats,
|
|
||||||
} from "@/utils/stats";
|
|
||||||
import useUser from "@/hooks/useUser";
|
import useUser from "@/hooks/useUser";
|
||||||
import Sidebar from "@/components/Sidebar";
|
|
||||||
import Diagnostic from "@/components/Diagnostic";
|
|
||||||
import {ToastContainer} from "react-toastify";
|
import {ToastContainer} from "react-toastify";
|
||||||
import {capitalize} from "lodash";
|
import {capitalize} from "lodash";
|
||||||
import {Module} from "@/interfaces";
|
import {Module} from "@/interfaces";
|
||||||
@@ -27,8 +16,11 @@ import Layout from "@/components/High/Layout";
|
|||||||
import {calculateAverageLevel, calculateBandScore} from "@/utils/score";
|
import {calculateAverageLevel, calculateBandScore} from "@/utils/score";
|
||||||
import {MODULE_ARRAY} from "@/utils/moduleUtils";
|
import {MODULE_ARRAY} from "@/utils/moduleUtils";
|
||||||
import {Chart} from "react-chartjs-2";
|
import {Chart} from "react-chartjs-2";
|
||||||
|
import useUsers from "@/hooks/useUsers";
|
||||||
|
import Select from "react-select";
|
||||||
|
import useGroups from "@/hooks/useGroups";
|
||||||
|
|
||||||
ChartJS.register(LinearScale, CategoryScale, PointElement, LineElement, Legend, Tooltip);
|
ChartJS.register(LinearScale, CategoryScale, PointElement, LineElement, LineController, Legend, Tooltip);
|
||||||
|
|
||||||
const COLORS = ["#1EB3FF", "#FF790A", "#3D9F11", "#EF5DA8"];
|
const COLORS = ["#1EB3FF", "#FF790A", "#3D9F11", "#EF5DA8"];
|
||||||
|
|
||||||
@@ -52,19 +44,16 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
|
|||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
export default function Stats() {
|
export default function Stats() {
|
||||||
const {user} = useUser({redirectTo: "/login"});
|
const [statsUserId, setStatsUserId] = useState<string>();
|
||||||
const {stats} = useStats(user?.id);
|
|
||||||
|
|
||||||
const totalExamsData = {
|
const {user} = useUser({redirectTo: "/login"});
|
||||||
labels: MODULE_ARRAY.map((x) => capitalize(x)),
|
const {users} = useUsers();
|
||||||
datasets: [
|
const {groups} = useGroups(user?.id);
|
||||||
{
|
const {stats} = useStats(statsUserId);
|
||||||
label: "Total exams",
|
|
||||||
data: MODULE_ARRAY.map((x) => totalExamsByModule(stats, x)),
|
useEffect(() => {
|
||||||
backgroundColor: ["#1EB3FF", "#FF790A", "#3D9F11", "#EF5DA8"],
|
if (user) setStatsUserId(user.id);
|
||||||
},
|
}, [user]);
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const calculateTotalScorePerSession = () => {
|
const calculateTotalScorePerSession = () => {
|
||||||
const groupedBySession = groupBySession(stats);
|
const groupedBySession = groupBySession(stats);
|
||||||
@@ -115,7 +104,7 @@ export default function Stats() {
|
|||||||
</Head>
|
</Head>
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
{user && (
|
{user && (
|
||||||
<Layout user={user}>
|
<Layout user={user} className="gap-8">
|
||||||
<section className="w-full flex gap-8">
|
<section className="w-full flex gap-8">
|
||||||
<img src={user.profilePicture} alt={user.name} className="aspect-square h-64 rounded-3xl drop-shadow-xl object-cover" />
|
<img src={user.profilePicture} alt={user.name} className="aspect-square h-64 rounded-3xl drop-shadow-xl object-cover" />
|
||||||
<div className="flex flex-col gap-4 py-4 w-full">
|
<div className="flex flex-col gap-4 py-4 w-full">
|
||||||
@@ -170,6 +159,22 @@ export default function Stats() {
|
|||||||
</section>
|
</section>
|
||||||
{stats.length > 0 && (
|
{stats.length > 0 && (
|
||||||
<section className="flex flex-col gap-3">
|
<section className="flex flex-col gap-3">
|
||||||
|
{(user.type === "developer" || user.type === "owner") && (
|
||||||
|
<Select
|
||||||
|
options={users.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))}
|
||||||
|
defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}}
|
||||||
|
onChange={(value) => setStatsUserId(value?.value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(user.type === "admin" || user.type === "teacher") && groups.length > 0 && (
|
||||||
|
<Select
|
||||||
|
options={users
|
||||||
|
.filter((x) => groups.flatMap((y) => y.participants).includes(x.id))
|
||||||
|
.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))}
|
||||||
|
defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}}
|
||||||
|
onChange={(value) => setStatsUserId(value?.value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<div className="flex gap-4 flex-wrap">
|
<div className="flex gap-4 flex-wrap">
|
||||||
{/* Exams per module */}
|
{/* Exams per module */}
|
||||||
<div className="flex flex-col gap-12 border w-full h-fit max-w-xs border-mti-gray-platinum p-4 pb-12 rounded-xl">
|
<div className="flex flex-col gap-12 border w-full h-fit max-w-xs border-mti-gray-platinum p-4 pb-12 rounded-xl">
|
||||||
|
|||||||
Reference in New Issue
Block a user