Added an initial filter for corporate on records

This commit is contained in:
Joao Ramos
2024-06-21 23:22:49 +01:00
parent 61a86394ed
commit e79139174b

View File

@@ -1,422 +1,614 @@
/* 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 {withIronSessionSsr} from "iron-session/next"; import { withIronSessionSsr } from "iron-session/next";
import {sessionOptions} from "@/lib/session"; import { sessionOptions } from "@/lib/session";
import {Stat, User} from "@/interfaces/user"; import { Stat, User } from "@/interfaces/user";
import {useEffect, useState} from "react"; import { useEffect, useState } from "react";
import useStats from "@/hooks/useStats"; import useStats from "@/hooks/useStats";
import {convertToUserSolutions, groupByDate} from "@/utils/stats"; import { convertToUserSolutions, groupByDate } from "@/utils/stats";
import moment from "moment"; import moment from "moment";
import useUsers from "@/hooks/useUsers"; import useUsers from "@/hooks/useUsers";
import useExamStore from "@/stores/examStore"; import useExamStore from "@/stores/examStore";
import {Module} from "@/interfaces"; import { Module } from "@/interfaces";
import {ToastContainer} from "react-toastify"; import { ToastContainer } from "react-toastify";
import {useRouter} from "next/router"; import { useRouter } from "next/router";
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 Layout from "@/components/High/Layout"; 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, BsClipboard, BsClock, BsHeadphones, BsMegaphone, BsPen, BsPersonDash, BsPersonFillX, BsXCircle} from "react-icons/bs"; import {
BsBook,
BsClipboard,
BsClock,
BsHeadphones,
BsMegaphone,
BsPen,
BsPersonDash,
BsPersonFillX,
BsXCircle,
} from "react-icons/bs";
import Select from "@/components/Low/Select"; import Select from "@/components/Low/Select";
import useGroups from "@/hooks/useGroups"; import useGroups from "@/hooks/useGroups";
import {shouldRedirectHome} from "@/utils/navigation.disabled"; import { shouldRedirectHome } from "@/utils/navigation.disabled";
import useAssignments from "@/hooks/useAssignments"; import useAssignments from "@/hooks/useAssignments";
import {uuidv4} from "@firebase/util"; import { uuidv4 } from "@firebase/util";
import {usePDFDownload} from "@/hooks/usePDFDownload"; import { usePDFDownload } from "@/hooks/usePDFDownload";
import useRecordStore from "@/stores/recordStore"; import useRecordStore from "@/stores/recordStore";
export const getServerSideProps = withIronSessionSsr(({req, res}) => { export const getServerSideProps = withIronSessionSsr(({ req, res }) => {
const user = req.session.user; const user = req.session.user;
if (!user || !user.isVerified) { if (!user || !user.isVerified) {
return { return {
redirect: { redirect: {
destination: "/login", destination: "/login",
permanent: false, permanent: false,
}, },
}; };
} }
if (shouldRedirectHome(user)) { if (shouldRedirectHome(user)) {
return { return {
redirect: { redirect: {
destination: "/", destination: "/",
permanent: false, permanent: false,
}, },
}; };
} }
return { return {
props: {user: req.session.user}, props: { user: req.session.user },
}; };
}, sessionOptions); }, sessionOptions);
export default function History({user}: {user: User}) { const defaultSelectableCorporate = {
const [statsUserId, setStatsUserId] = useRecordStore((state) => [state.selectedUser, state.setSelectedUser]); value: "",
// const [statsUserId, setStatsUserId] = useState<string | undefined>(user.id); label: "All",
const [groupedStats, setGroupedStats] = useState<{[key: string]: Stat[]}>(); };
const [filter, setFilter] = useState<"months" | "weeks" | "days" | "assignments">();
const {assignments} = useAssignments({});
const {users} = useUsers(); export default function History({ user }: { user: User }) {
const {stats, isLoading: isStatsLoading} = useStats(statsUserId); const [statsUserId, setStatsUserId] = useRecordStore((state) => [
const {groups} = useGroups(user.id); state.selectedUser,
state.setSelectedUser,
]);
// const [statsUserId, setStatsUserId] = useState<string | undefined>(user.id);
const [groupedStats, setGroupedStats] = useState<{ [key: string]: Stat[] }>();
const [filter, setFilter] = useState<
"months" | "weeks" | "days" | "assignments"
>();
const { assignments } = useAssignments({});
const setExams = useExamStore((state) => state.setExams); const { users } = useUsers();
const setShowSolutions = useExamStore((state) => state.setShowSolutions); const { stats, isLoading: isStatsLoading } = useStats(statsUserId);
const setUserSolutions = useExamStore((state) => state.setUserSolutions); const { groups: allGroups } = useGroups();
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(() => { const groups = allGroups.filter((x) => x.admin === user.id);
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(() => { const setExams = useExamStore((state) => state.setExams);
// just set this initially const setShowSolutions = useExamStore((state) => state.setShowSolutions);
if(!statsUserId) setStatsUserId(user.id); 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 toggleFilter = (value: "months" | "weeks" | "days" | "assignments") => { useEffect(() => {
setFilter((prev) => (prev === value ? undefined : value)); 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]);
const filterStatsByDate = (stats: {[key: string]: Stat[]}) => { // useEffect(() => {
if (filter && filter !== "assignments") { // // just set this initially
const filterDate = moment() // if (!statsUserId) setStatsUserId(user.id);
.subtract({[filter as string]: 1}) // }, []);
.format("x");
const filteredStats: {[key: string]: Stat[]} = {};
Object.keys(stats).forEach((timestamp) => { const toggleFilter = (value: "months" | "weeks" | "days" | "assignments") => {
if (timestamp >= filterDate) filteredStats[timestamp] = stats[timestamp]; setFilter((prev) => (prev === value ? undefined : value));
}); };
return filteredStats; 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[] } = {};
if (filter && filter === "assignments") { Object.keys(stats).forEach((timestamp) => {
const filteredStats: {[key: string]: Stat[]} = {}; if (timestamp >= filterDate)
filteredStats[timestamp] = stats[timestamp];
});
Object.keys(stats).forEach((timestamp) => { return filteredStats;
if (stats[timestamp].map((s) => s.assignment === undefined).includes(false)) }
filteredStats[timestamp] = [...stats[timestamp].filter((s) => !!s.assignment)];
});
return filteredStats; if (filter && filter === "assignments") {
} const filteredStats: { [key: string]: Stat[] } = {};
return stats; Object.keys(stats).forEach((timestamp) => {
}; if (
stats[timestamp]
.map((s) => s.assignment === undefined)
.includes(false)
)
filteredStats[timestamp] = [
...stats[timestamp].filter((s) => !!s.assignment),
];
});
const formatTimestamp = (timestamp: string) => { return filteredStats;
const date = moment(parseInt(timestamp)); }
const formatter = "YYYY/MM/DD - HH:mm";
return date.format(formatter); return stats;
}; };
const aggregateScoresByModule = (stats: Stat[]): {module: Module; total: number; missing: number; correct: number}[] => { const formatTimestamp = (timestamp: string) => {
const scores: {[key in Module]: {total: number; missing: number; correct: number}} = { const date = moment(parseInt(timestamp));
reading: { const formatter = "YYYY/MM/DD - HH:mm";
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) => { return date.format(formatter);
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) const aggregateScoresByModule = (
.filter((x) => scores[x as Module].total > 0) stats: Stat[]
.map((x) => ({module: x as Module, ...scores[x as Module]})); ): { 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 customContent = (timestamp: string) => { stats.forEach((x) => {
if (!groupedStats) return <></>; 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,
};
});
const dateStats = groupedStats[timestamp]; return Object.keys(scores)
const correct = dateStats.reduce((accumulator, current) => accumulator + current.score.correct, 0); .filter((x) => scores[x as Module].total > 0)
const total = dateStats.reduce((accumulator, current) => accumulator + current.score.total, 0); .map((x) => ({ module: x as Module, ...scores[x as Module] }));
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) => ({ const customContent = (timestamp: string) => {
module: x.module, if (!groupedStats) return <></>;
level: calculateBandScore(x.correct, x.total, x.module, user.focus),
}));
const {timeSpent, inactivity, session} = dateStats[0]; 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 selectExam = () => { const aggregatedLevels = aggregatedScores.map((x) => ({
const examPromises = uniqBy(dateStats, "exam").map((stat) => { module: x.module,
console.log({stat}); level: calculateBandScore(x.correct, x.total, x.module, user.focus),
return getExamById(stat.module, stat.exam); }));
});
Promise.all(examPromises).then((exams) => { const { timeSpent, inactivity, session } = dateStats[0];
if (exams.every((x) => !!x)) {
if (!!timeSpent) setTimeSpent(timeSpent);
if (!!inactivity) setInactivity(inactivity);
setUserSolutions(convertToUserSolutions(dateStats)); const selectExam = () => {
setShowSolutions(true); const examPromises = uniqBy(dateStats, "exam").map((stat) => {
setExams(exams.map((x) => x!).sort(sortByModule)); console.log({ stat });
setSelectedModules( return getExamById(stat.module, stat.exam);
exams });
.map((x) => x!)
.sort(sortByModule)
.map((x) => x!.module),
);
router.push("/exercises");
}
});
};
const textColor = clsx( Promise.all(examPromises).then((exams) => {
correct / total >= 0.7 && "text-mti-purple", if (exams.every((x) => !!x)) {
correct / total >= 0.3 && correct / total < 0.7 && "text-mti-red", if (!!timeSpent) setTimeSpent(timeSpent);
correct / total < 0.3 && "text-mti-rose", if (!!inactivity) setInactivity(inactivity);
);
const content = ( setUserSolutions(convertToUserSolutions(dateStats));
<> setShowSolutions(true);
<div className="w-full flex justify-between -md:items-center 2xl:items-center"> setExams(exams.map((x) => x!).sort(sortByModule));
<div className="flex flex-col md:gap-1 -md:gap-2 2xl:gap-2"> setSelectedModules(
<span className="font-medium">{formatTimestamp(timestamp)}</span> exams
<div className="flex items-center gap-2"> .map((x) => x!)
{!!timeSpent && ( .sort(sortByModule)
<span className="text-sm flex gap-2 items-center tooltip" data-tip="Time Spent"> .map((x) => x!.module)
<BsClock /> {Math.floor(timeSpent / 60)} minutes );
</span> router.push("/exercises");
)} }
{!!inactivity && ( });
<span className="text-sm flex gap-2 items-center tooltip" data-tip="Inactivity"> };
<BsXCircle /> {Math.floor(inactivity / 60)} minutes
</span>
)}
</div>
</div>
<div className="flex flex-row gap-2">
<span className={textColor}>
Level{" "}
{(aggregatedLevels.reduce((accumulator, current) => accumulator + current.level, 0) / aggregatedLevels.length).toFixed(1)}
</span>
{renderPdfIcon(session, textColor, textColor)}
</div>
</div>
<div className="w-full flex flex-col gap-1"> const textColor = clsx(
<div className="grid grid-cols-4 gap-2 place-items-start w-full -md:mt-2"> correct / total >= 0.7 && "text-mti-purple",
{aggregatedLevels.map(({module, level}) => ( correct / total >= 0.3 && correct / total < 0.7 && "text-mti-red",
<div correct / total < 0.3 && "text-mti-rose"
key={module} );
className={clsx(
"flex gap-2 items-center w-fit text-white -md:px-4 xl:px-4 md:px-2 py-2 rounded-xl",
module === "reading" && "bg-ielts-reading",
module === "listening" && "bg-ielts-listening",
module === "writing" && "bg-ielts-writing",
module === "speaking" && "bg-ielts-speaking",
module === "level" && "bg-ielts-level",
)}>
{module === "reading" && <BsBook className="w-4 h-4" />}
{module === "listening" && <BsHeadphones className="w-4 h-4" />}
{module === "writing" && <BsPen className="w-4 h-4" />}
{module === "speaking" && <BsMegaphone className="w-4 h-4" />}
{module === "level" && <BsClipboard className="w-4 h-4" />}
<span className="text-sm">{level.toFixed(1)}</span>
</div>
))}
</div>
{assignment && ( const content = (
<span className="font-light text-sm"> <>
Assignment: {assignment.name}, Teacher: {users.find((u) => u.id === assignment.assigner)?.name} <div className="w-full flex justify-between -md:items-center 2xl:items-center">
</span> <div className="flex flex-col md:gap-1 -md:gap-2 2xl:gap-2">
)} <span className="font-medium">{formatTimestamp(timestamp)}</span>
</div> <div className="flex items-center gap-2">
</> {!!timeSpent && (
); <span
className="text-sm flex gap-2 items-center tooltip"
data-tip="Time Spent"
>
<BsClock /> {Math.floor(timeSpent / 60)} minutes
</span>
)}
{!!inactivity && (
<span
className="text-sm flex gap-2 items-center tooltip"
data-tip="Inactivity"
>
<BsXCircle /> {Math.floor(inactivity / 60)} minutes
</span>
)}
</div>
</div>
<div className="flex flex-row gap-2">
<span className={textColor}>
Level{" "}
{(
aggregatedLevels.reduce(
(accumulator, current) => accumulator + current.level,
0
) / aggregatedLevels.length
).toFixed(1)}
</span>
{renderPdfIcon(session, textColor, textColor)}
</div>
</div>
return ( <div className="w-full flex flex-col gap-1">
<> <div className="grid grid-cols-4 gap-2 place-items-start w-full -md:mt-2">
<div {aggregatedLevels.map(({ module, level }) => (
key={uuidv4()} <div
className={clsx( key={module}
"flex flex-col justify-between gap-4 border border-mti-gray-platinum p-4 cursor-pointer rounded-xl transition ease-in-out duration-300 -md:hidden", className={clsx(
isDisabled && "grayscale tooltip", "flex gap-2 items-center w-fit text-white -md:px-4 xl:px-4 md:px-2 py-2 rounded-xl",
correct / total >= 0.7 && "hover:border-mti-purple", module === "reading" && "bg-ielts-reading",
correct / total >= 0.3 && correct / total < 0.7 && "hover:border-mti-red", module === "listening" && "bg-ielts-listening",
correct / total < 0.3 && "hover:border-mti-rose", module === "writing" && "bg-ielts-writing",
)} module === "speaking" && "bg-ielts-speaking",
onClick={isDisabled ? () => null : selectExam} module === "level" && "bg-ielts-level"
data-tip="This exam is still being evaluated..." )}
role="button"> >
{content} {module === "reading" && <BsBook className="w-4 h-4" />}
</div> {module === "listening" && <BsHeadphones className="w-4 h-4" />}
<div {module === "writing" && <BsPen className="w-4 h-4" />}
key={uuidv4()} {module === "speaking" && <BsMegaphone className="w-4 h-4" />}
className={clsx( {module === "level" && <BsClipboard className="w-4 h-4" />}
"flex flex-col gap-4 border border-mti-gray-platinum p-4 cursor-pointer rounded-xl transition ease-in-out duration-300 -md:tooltip md:hidden", <span className="text-sm">{level.toFixed(1)}</span>
correct / total >= 0.7 && "hover:border-mti-purple", </div>
correct / total >= 0.3 && correct / total < 0.7 && "hover:border-mti-red", ))}
correct / total < 0.3 && "hover:border-mti-rose", </div>
)}
data-tip="Your screen size is too small to view previous exams."
role="button">
{content}
</div>
</>
);
};
const selectedUser = users.find((x) => x.id === statsUserId) || user; {assignment && (
return ( <span className="font-light text-sm">
<> Assignment: {assignment.name}, Teacher:{" "}
<Head> {users.find((u) => u.id === assignment.assigner)?.name}
<title>Record | EnCoach</title> </span>
<meta )}
name="description" </div>
content="A training platform for the IELTS exam provided by the Muscat Training Institute and developed by eCrop." </>
/> );
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" /> return (
</Head> <>
<ToastContainer /> <div
{user && ( key={uuidv4()}
<Layout user={user}> className={clsx(
<div className="w-full flex -xl:flex-col -xl:gap-4 justify-between items-center"> "flex flex-col justify-between gap-4 border border-mti-gray-platinum p-4 cursor-pointer rounded-xl transition ease-in-out duration-300 -md:hidden",
<div className="xl:w-3/4"> isDisabled && "grayscale tooltip",
{(user.type === "developer" || user.type === "admin") && ( correct / total >= 0.7 && "hover:border-mti-purple",
<Select correct / total >= 0.3 &&
options={users.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))} correct / total < 0.7 &&
value={{value: selectedUser.id, label: `${selectedUser.name} - ${selectedUser.email}`}} "hover:border-mti-red",
onChange={(value) => setStatsUserId(value?.value)} correct / total < 0.3 && "hover:border-mti-rose"
styles={{ )}
menuPortal: (base) => ({...base, zIndex: 9999}), onClick={isDisabled ? () => null : selectExam}
option: (styles, state) => ({ data-tip="This exam is still being evaluated..."
...styles, role="button"
backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white", >
color: state.isFocused ? "black" : styles.color, {content}
}), </div>
}} <div
/> key={uuidv4()}
)} className={clsx(
{(user.type === "corporate" || user.type === "teacher") && groups.length > 0 && ( "flex flex-col gap-4 border border-mti-gray-platinum p-4 cursor-pointer rounded-xl transition ease-in-out duration-300 -md:tooltip md:hidden",
<Select correct / total >= 0.7 && "hover:border-mti-purple",
options={users correct / total >= 0.3 &&
.filter((x) => groups.flatMap((y) => y.participants).includes(x.id)) correct / total < 0.7 &&
.map((x) => ({value: x.id, label: `${x.name} - ${x.email}`}))} "hover:border-mti-red",
value={{value: selectedUser.id, label: `${selectedUser.name} - ${selectedUser.email}`}} correct / total < 0.3 && "hover:border-mti-rose"
onChange={(value) => setStatsUserId(value?.value)} )}
styles={{ data-tip="Your screen size is too small to view previous exams."
menuPortal: (base) => ({...base, zIndex: 9999}), role="button"
option: (styles, state) => ({ >
...styles, {content}
backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white", </div>
color: state.isFocused ? "black" : styles.color, </>
}), );
}} };
/>
)} const selectableCorporates = [
</div> defaultSelectableCorporate,
<div className="flex gap-4 w-full justify-center xl:justify-end"> ...users
<button .filter((x) => x.type === "corporate")
className={clsx( .map((x) => ({
"bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light", value: x.id,
"transition duration-300 ease-in-out", label: `${x.name} - ${x.email}`,
filter === "assignments" && "!bg-mti-purple-light !text-white", })),
)} ];
onClick={() => toggleFilter("assignments")}>
Assignments const [selectedCorporate, setSelectedCorporate] = useState<string>(
</button> defaultSelectableCorporate.value
<button );
className={clsx(
"bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light", const getUsersList = (): User[] => {
"transition duration-300 ease-in-out", if (selectedCorporate) {
filter === "months" && "!bg-mti-purple-light !text-white", // get groups for that corporate
)} const selectedCorporateGroups = allGroups.filter(
onClick={() => toggleFilter("months")}> (x) => x.admin === selectedCorporate
Last month );
</button>
<button // get the teacher ids for that group
className={clsx( const selectedCorporateGroupsParticipants =
"bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light", selectedCorporateGroups.flatMap((x) => x.participants);
"transition duration-300 ease-in-out",
filter === "weeks" && "!bg-mti-purple-light !text-white", // // search for groups for these teachers
)} // const teacherGroups = allGroups.filter((x) => {
onClick={() => toggleFilter("weeks")}> // return selectedCorporateGroupsParticipants.includes(x.admin);
Last week // });
</button>
<button // const usersList = [
className={clsx( // ...selectedCorporateGroupsParticipants,
"bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light", // ...teacherGroups.flatMap((x) => x.participants),
"transition duration-300 ease-in-out", // ];
filter === "days" && "!bg-mti-purple-light !text-white", const userListWithUsers = selectedCorporateGroupsParticipants.map((x) =>
)} users.find((y) => y.id === x)
onClick={() => toggleFilter("days")}> ) as User[];
Last day return userListWithUsers.filter((x) => x);
</button> }
</div>
</div> return users || [];
{groupedStats && Object.keys(groupedStats).length > 0 && !isStatsLoading && ( };
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 w-full gap-4 xl:gap-6">
{Object.keys(filterStatsByDate(groupedStats)) const corporateFilteredUserList = getUsersList();
.sort((a, b) => parseInt(b) - parseInt(a))
.map(customContent)} const getSelectedUser = () => {
</div> if (selectedCorporate) {
)} const userInCorporate = corporateFilteredUserList.find(
{groupedStats && Object.keys(groupedStats).length === 0 && !isStatsLoading && ( (x) => x.id === statsUserId
<span className="font-semibold ml-1">No record to display...</span> );
)} return userInCorporate || corporateFilteredUserList[0];
</Layout> }
)}
</> 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 (
<>
<Head>
<title>Record | EnCoach</title>
<meta
name="description"
content="A training platform for the IELTS exam provided by the Muscat Training Institute and developed by eCrop."
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<ToastContainer />
{user && (
<Layout user={user}>
<div className="w-full flex -xl:flex-col -xl:gap-4 justify-between items-center">
<div className="xl:w-3/4">
{(user.type === "developer" || user.type === "admin") && (
<>
<label className="font-normal text-base text-mti-gray-dim">
Corporate
</label>
<Select
options={selectableCorporates}
value={selectableCorporates.find(
(x) => x.value === selectedCorporate
)}
onChange={(value) =>
setSelectedCorporate(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,
}),
}}
></Select>
<label className="font-normal text-base text-mti-gray-dim">
User
</label>
<Select
options={corporateFilteredUserList.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,
}),
}}
/>
</>
)}
{(user.type === "corporate" || user.type === "teacher") &&
groups.length > 0 && (
<>
<label className="font-normal text-base text-mti-gray-dim">
User
</label>
<Select
options={users
.filter((x) =>
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,
}),
}}
/>
</>
)}
</div>
<div className="flex gap-4 w-full justify-center xl:justify-end">
<button
className={clsx(
"bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light",
"transition duration-300 ease-in-out",
filter === "assignments" && "!bg-mti-purple-light !text-white"
)}
onClick={() => toggleFilter("assignments")}
>
Assignments
</button>
<button
className={clsx(
"bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light",
"transition duration-300 ease-in-out",
filter === "months" && "!bg-mti-purple-light !text-white"
)}
onClick={() => toggleFilter("months")}
>
Last month
</button>
<button
className={clsx(
"bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light",
"transition duration-300 ease-in-out",
filter === "weeks" && "!bg-mti-purple-light !text-white"
)}
onClick={() => toggleFilter("weeks")}
>
Last week
</button>
<button
className={clsx(
"bg-mti-purple-ultralight text-mti-purple px-4 py-2 rounded-full hover:text-white hover:bg-mti-purple-light",
"transition duration-300 ease-in-out",
filter === "days" && "!bg-mti-purple-light !text-white"
)}
onClick={() => toggleFilter("days")}
>
Last day
</button>
</div>
</div>
{groupedStats &&
Object.keys(groupedStats).length > 0 &&
!isStatsLoading && (
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 w-full gap-4 xl:gap-6">
{Object.keys(filterStatsByDate(groupedStats))
.sort((a, b) => parseInt(b) - parseInt(a))
.map(customContent)}
</div>
)}
{groupedStats &&
Object.keys(groupedStats).length === 0 &&
!isStatsLoading && (
<span className="font-semibold ml-1">
No record to display...
</span>
)}
</Layout>
)}
</>
);
} }