diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx
index 761ebd7d..56a5f83a 100644
--- a/src/components/Sidebar.tsx
+++ b/src/components/Sidebar.tsx
@@ -1,249 +1,249 @@
import clsx from "clsx";
-import {IconType} from "react-icons";
-import {MdSpaceDashboard} from "react-icons/md";
+import { IconType } from "react-icons";
+import { MdSpaceDashboard } from "react-icons/md";
import {
- BsFileEarmarkText,
- BsClockHistory,
- BsPencil,
- BsGraphUp,
- BsChevronBarRight,
- BsChevronBarLeft,
- BsShieldFill,
- BsCloudFill,
- BsCurrencyDollar,
- BsClipboardData,
- BsFileLock,
- BsPeople,
+ BsFileEarmarkText,
+ BsClockHistory,
+ BsPencil,
+ BsGraphUp,
+ BsChevronBarRight,
+ BsChevronBarLeft,
+ BsShieldFill,
+ BsCloudFill,
+ BsCurrencyDollar,
+ BsClipboardData,
+ BsFileLock,
+ BsPeople,
} from "react-icons/bs";
-import {CiDumbbell} from "react-icons/ci";
-import {RiLogoutBoxFill} from "react-icons/ri";
-import {SlPencil} from "react-icons/sl";
-import {FaAward} from "react-icons/fa";
+import { CiDumbbell } from "react-icons/ci";
+import { RiLogoutBoxFill } from "react-icons/ri";
+import { SlPencil } from "react-icons/sl";
+import { FaAward } from "react-icons/fa";
import Link from "next/link";
-import {useRouter} from "next/router";
+import { useRouter } from "next/router";
import axios from "axios";
import FocusLayer from "@/components/FocusLayer";
-import {preventNavigation} from "@/utils/navigation.disabled";
-import {useEffect, useState} from "react";
+import { preventNavigation } from "@/utils/navigation.disabled";
+import { useEffect, useState } from "react";
import usePreferencesStore from "@/stores/preferencesStore";
-import {User} from "@/interfaces/user";
+import { User } from "@/interfaces/user";
import useTicketsListener from "@/hooks/useTicketsListener";
-import {checkAccess, getTypesOfUser} from "@/utils/permissions";
+import { checkAccess, getTypesOfUser } from "@/utils/permissions";
import usePermissions from "@/hooks/usePermissions";
interface Props {
- path: string;
- navDisabled?: boolean;
- focusMode?: boolean;
- onFocusLayerMouseEnter?: () => void;
- className?: string;
- user: User;
+ path: string;
+ navDisabled?: boolean;
+ focusMode?: boolean;
+ onFocusLayerMouseEnter?: () => void;
+ className?: string;
+ user: User;
}
interface NavProps {
- Icon: IconType;
- label: string;
- path: string;
- keyPath: string;
- disabled?: boolean;
- isMinimized?: boolean;
- badge?: number;
+ Icon: IconType;
+ label: string;
+ path: string;
+ keyPath: string;
+ disabled?: boolean;
+ isMinimized?: boolean;
+ badge?: number;
}
-const Nav = ({Icon, label, path, keyPath, disabled = false, isMinimized = false, badge}: NavProps) => {
- return (
-
-
- {!isMinimized && {label}}
- {!!badge && badge > 0 && (
-
-
-
- {checkAccess(user, ["student", "teacher", "developer"], permissions, "viewExams") && (
-
- )}
- {checkAccess(user, ["student", "teacher", "developer"], permissions, "viewExercises") && (
-
- )}
- {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewStats") && (
-
- )}
- {checkAccess(user, ["developer", "admin", "mastercorporate", "corporate", "teacher", "student"], permissions) && (
-
- )}
- {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
-
- )}
- {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
-
- )}
- {checkAccess(user, ["admin", "developer", "agent", "corporate", "mastercorporate"], permissions, "viewPaymentRecords") && (
-
- )}
- {checkAccess(user, ["admin", "developer", "corporate", "teacher", "mastercorporate"]) && (
-
- )}
- {checkAccess(user, ["admin", "developer", "agent"], permissions, "viewTickets") && (
-
- )}
- {checkAccess(user, ["developer", "admin", "corporate", "mastercorporate"]) && (
-
- )}
- {checkAccess(user, ["developer", "admin", "corporate", "mastercorporate", "agent"]) && (
-
- )}
-
-
-
-
-
- {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewStats") && (
-
- )}
- {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
-
- )}
- {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
-
- )}
- {checkAccess(user, getTypesOfUser(["student"])) && (
-
- )}
- {checkAccess(user, getTypesOfUser(["student"])) && (
-
- )}
- {checkAccess(user, ["developer"]) && (
- <>
-
-
- >
- )}
-
+ return (
+
+
+
+ {checkAccess(user, ["student", "teacher", "developer"], permissions, "viewExams") && (
+
+ )}
+ {checkAccess(user, ["student", "teacher", "developer"], permissions, "viewExercises") && (
+
+ )}
+ {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewStats") && (
+
+ )}
+ {checkAccess(user, ["developer", "admin", "mastercorporate", "corporate", "teacher", "student"], permissions) && (
+
+ )}
+ {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
+
+ )}
+ {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
+
+ )}
+ {checkAccess(user, ["admin", "developer", "agent", "corporate", "mastercorporate"], permissions, "viewPaymentRecords") && (
+
+ )}
+ {checkAccess(user, ["admin", "developer", "corporate", "teacher", "mastercorporate"]) && (
+
+ )}
+ {checkAccess(user, ["admin", "developer", "agent"], permissions, "viewTickets") && (
+
+ )}
+ {checkAccess(user, ["developer", "admin", "corporate", "mastercorporate"]) && (
+
+ )}
+ {checkAccess(user, ["developer", "admin", "corporate", "mastercorporate", "agent"]) && (
+
+ )}
+
+
+
+
+
+ {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewStats") && (
+
+ )}
+ {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
+
+ )}
+ {checkAccess(user, getTypesOfUser(["agent"]), permissions, "viewRecords") && (
+
+ )}
+ {checkAccess(user, getTypesOfUser(["student"])) && (
+
+ )}
+ {checkAccess(user, getTypesOfUser(["student"])) && (
+
+ )}
+ {checkAccess(user, ["developer"]) && (
+ <>
+
+
+ >
+ )}
+
-
-
- {isMinimized ? : }
- {!isMinimized && Minimize}
-
-
{} : logout}
- className={clsx(
- "hover:text-mti-rose flex cursor-pointer items-center gap-4 rounded-full p-4 text-black transition duration-300 ease-in-out",
- isMinimized ? "w-fit" : "w-full min-w-[250px] px-8",
- )}>
-
- {!isMinimized && Log Out}
-
-
- {focusMode && }
-
- );
+
+
+ {isMinimized ? : }
+ {!isMinimized && Minimize}
+
+
{ } : logout}
+ className={clsx(
+ "hover:text-mti-rose flex cursor-pointer items-center gap-4 rounded-full p-4 text-black transition duration-300 ease-in-out",
+ isMinimized ? "w-fit" : "w-full min-w-[250px] px-8",
+ )}>
+
+ {!isMinimized && Log Out}
+
+
+ {focusMode && }
+
+ );
}
diff --git a/src/dashboards/AssignmentCard.tsx b/src/dashboards/AssignmentCard.tsx
index d3243656..3bd07d67 100644
--- a/src/dashboards/AssignmentCard.tsx
+++ b/src/dashboards/AssignmentCard.tsx
@@ -49,7 +49,6 @@ export default function AssignmentCard({
const renderUnarchiveIcon = useAssignmentUnarchive(id, reload);
const renderReleaseIcon = useAssignmentRelease(id, reload);
-
const calculateAverageModuleScore = (module: Module) => {
const resultModuleBandScores = results.map((r) => {
const moduleStats = r.stats.filter((s) => s.module === module);
@@ -65,26 +64,26 @@ export default function AssignmentCard({
const uniqModules = uniqBy(exams, (x) => x.module);
const shouldRenderPDF = () => {
- if(released && allowDownload) {
+ if (released && allowDownload) {
// in order to be downloadable, the assignment has to be released
// the component should have the allowDownload prop
// and the assignment should not have the level module
- return uniqModules.every(({ module }) => module !== 'level');
+ return uniqModules.every(({module}) => module !== "level");
}
return false;
- }
+ };
const shouldRenderExcel = () => {
- if(released && allowExcelDownload) {
+ if (released && allowExcelDownload) {
// in order to be downloadable, the assignment has to be released
// the component should have the allowExcelDownload prop
// and the assignment should have the level module
- return uniqModules.some(({ module }) => module === 'level');
+ return uniqModules.some(({module}) => module === "level");
}
return false;
- }
+ };
return (
{
+ const user = req.session.user as User | undefined;
+
+ if (!user) {
+ return {
+ redirect: {
+ destination: "/login",
+ permanent: false,
+ },
+ };
+ }
+
+ if (!checkAccess(user, ["admin", "developer", "corporate", "teacher", "mastercorporate"]))
+ return {
+ redirect: {
+ destination: "/dashboard",
+ permanent: false,
+ },
+ };
+
+ res.setHeader("Cache-Control", "public, s-maxage=10, stale-while-revalidate=59");
+
+ const {id} = params as {id: string};
+ const entityIDS = mapBy(user.entities, "id") || [];
+
+ const assignment = await getAssignment(id);
+ if (!assignment)
+ return {
+ redirect: {
+ destination: "/assignments",
+ permanent: false,
+ },
+ };
+
+ const users = await (checkAccess(user, ["developer", "admin"]) ? getUsers() : getEntitiesUsers(entityIDS));
+ const entities = await (checkAccess(user, ["developer", "admin"]) ? getEntitiesWithRoles() : getEntitiesWithRoles(entityIDS));
+ const groups = await (checkAccess(user, ["developer", "admin"]) ? getGroups() : getGroupsByEntities(entityIDS));
+
+ return {props: serialize({user, users, entities, assignment, groups})};
+}, sessionOptions);
+
+interface Props {
+ user: User;
+ users: User[];
+ assignment: Assignment;
+ groups: Group[];
+ entities: EntityWithRoles[];
+}
+
+export default function AssignmentView({user, users, entities, groups, assignment}: Props) {
+ 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 router = useRouter();
+
+ const deleteAssignment = async () => {
+ if (!confirm("Are you sure you want to delete this assignment?")) return;
+
+ axios
+ .delete(`/api/assignments/${assignment?.id}`)
+ .then(() => toast.success(`Successfully deleted the assignment "${assignment?.name}".`))
+ .catch(() => toast.error("Something went wrong, please try again later."))
+ .finally(() => router.push("/assignments"));
+ };
+
+ const startAssignment = () => {
+ if (assignment) {
+ axios
+ .post(`/api/assignments/${assignment.id}/start`)
+ .then(() => {
+ toast.success(`The assignment "${assignment.name}" has been started successfully!`);
+ router.replace(router.asPath);
+ })
+ .catch((e) => {
+ console.log(e);
+ toast.error("Something went wrong, please try again later!");
+ });
+ }
+ };
+
+ const formatTimestamp = (timestamp: string) => {
+ const date = moment(parseInt(timestamp));
+ const formatter = "YYYY/MM/DD - HH:mm";
+
+ return date.format(formatter);
+ };
+
+ const calculateAverageModuleScore = (module: Module) => {
+ if (!assignment) return -1;
+
+ const resultModuleBandScores = assignment.results.map((r) => {
+ const moduleStats = r.stats.filter((s) => s.module === module);
+
+ const correct = moduleStats.reduce((acc, curr) => acc + curr.score.correct, 0);
+ const total = moduleStats.reduce((acc, curr) => acc + curr.score.total, 0);
+ return calculateBandScore(correct, total, module, r.type);
+ });
+
+ return resultModuleBandScores.length === 0 ? -1 : resultModuleBandScores.reduce((acc, curr) => acc + curr, 0) / assignment.results.length;
+ };
+
+ 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,
+ };
+ });
+
+ return Object.keys(scores)
+ .filter((x) => scores[x as Module].total > 0)
+ .map((x) => ({module: x as Module, ...scores[x as Module]}));
+ };
+
+ const customContent = (stats: Stat[], user: string, focus: "academic" | "general") => {
+ const correct = stats.reduce((accumulator, current) => accumulator + current.score.correct, 0);
+ const total = stats.reduce((accumulator, current) => accumulator + current.score.total, 0);
+ const aggregatedScores = aggregateScoresByModule(stats).filter((x) => x.total > 0);
+
+ const aggregatedLevels = aggregatedScores.map((x) => ({
+ module: x.module,
+ level: calculateBandScore(x.correct, x.total, x.module, focus),
+ }));
+
+ const timeSpent = stats[0].timeSpent;
+
+ const selectExam = () => {
+ const examPromises = uniqBy(stats, "exam").map((stat) => getExamById(stat.module, stat.exam));
+
+ Promise.all(examPromises).then((exams) => {
+ if (exams.every((x) => !!x)) {
+ setUserSolutions(convertToUserSolutions(stats));
+ 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(stats[0].date.toString())}
+ {timeSpent && (
+ <>
+ •
+ {Math.floor(timeSpent / 60)} minutes
+ >
+ )}
+
+
= 0.7 && "text-mti-purple",
+ correct / total >= 0.3 && correct / total < 0.7 && "text-mti-red",
+ correct / total < 0.3 && "text-mti-rose",
+ )}>
+ Level{" "}
+ {(aggregatedLevels.reduce((accumulator, current) => accumulator + current.level, 0) / aggregatedLevels.length).toFixed(1)}
+
+
+
+
+
+ {aggregatedLevels.map(({module, level}) => (
+
+ {module === "reading" && }
+ {module === "listening" && }
+ {module === "writing" && }
+ {module === "speaking" && }
+ {module === "level" && }
+ {level.toFixed(1)}
+
+ ))}
+
+
+ >
+ );
+
+ return (
+
+
+ {(() => {
+ const student = users.find((u) => u.id === user);
+ return `${student?.name} (${student?.email})`;
+ })()}
+
+
= 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}
+ 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 shouldRenderStart = () => {
+ if (assignment) {
+ if (futureAssignmentFilter(assignment)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ return (
+ <>
+
+
{assignment.name} | EnCoach
+
+
+
+
+
+
+
+
+
+
+
{assignment.name}
+
+
+
+
+
+
+
+ Start Date: {moment(assignment?.startDate).format("DD/MM/YY, HH:mm")}
+ End Date: {moment(assignment?.endDate).format("DD/MM/YY, HH:mm")}
+
+
+
+ Assignees:{" "}
+ {users
+ .filter((u) => assignment?.assignees.includes(u.id))
+ .map((u) => `${u.name} (${u.email})`)
+ .join(", ")}
+
+ Assigner: {getUserName(users.find((x) => x.id === assignment?.assigner))}
+
+
+
+
Average Scores
+
+ {assignment &&
+ uniqBy(assignment.exams, (x) => x.module).map(({module}) => (
+
+ {module === "reading" && }
+ {module === "listening" && }
+ {module === "writing" && }
+ {module === "speaking" && }
+ {module === "level" && }
+ {calculateAverageModuleScore(module) > -1 && (
+ {calculateAverageModuleScore(module).toFixed(1)}
+ )}
+
+ ))}
+
+
+
+
+ Results ({assignment?.results.length}/{assignment?.assignees.length})
+
+
+ {assignment && assignment?.results.length > 0 && (
+
+ {assignment.results.map((r) => customContent(r.stats, r.user, r.type))}
+
+ )}
+ {assignment && assignment?.results.length === 0 &&
No results yet...}
+
+
+
+
+ {assignment &&
+ (assignment.results.length === assignment.assignees.length || moment().isAfter(moment(assignment.endDate))) && (
+
+ )}
+ {/** if the assignment is not deemed as active yet, display start */}
+ {shouldRenderStart() && (
+
+ )}
+
+
+
+
+ >
+ );
+}
diff --git a/src/pages/assignments/creator/[id].tsx b/src/pages/assignments/creator/[id].tsx
index 3b232bcc..ae69ec03 100644
--- a/src/pages/assignments/creator/[id].tsx
+++ b/src/pages/assignments/creator/[id].tsx
@@ -4,6 +4,7 @@ import Checkbox from "@/components/Low/Checkbox";
import Input from "@/components/Low/Input";
import ProgressBar from "@/components/Low/ProgressBar";
import Select from "@/components/Low/Select";
+import Separator from "@/components/Low/Separator";
import useExams from "@/hooks/useExams";
import {useListSearch} from "@/hooks/useListSearch";
import usePagination from "@/hooks/usePagination";
@@ -16,20 +17,22 @@ import {sessionOptions} from "@/lib/session";
import {mapBy, serialize} from "@/utils";
import {getAssignment} from "@/utils/assignments.be";
import {getEntitiesWithRoles} from "@/utils/entities.be";
-import {getGroupsByEntities} from "@/utils/groups.be";
+import {getGroups, getGroupsByEntities} from "@/utils/groups.be";
import {checkAccess} from "@/utils/permissions";
import {calculateAverageLevel} from "@/utils/score";
-import {getEntitiesUsers} from "@/utils/users.be";
+import {getEntitiesUsers, getUsers} from "@/utils/users.be";
import axios from "axios";
import clsx from "clsx";
import {withIronSessionSsr} from "iron-session/next";
import {capitalize} from "lodash";
import moment from "moment";
+import Head from "next/head";
+import Link from "next/link";
import {useRouter} from "next/router";
import {generate} from "random-words";
import {useEffect, useMemo, useState} from "react";
import ReactDatePicker from "react-datepicker";
-import {BsBook, BsCheckCircle, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle} from "react-icons/bs";
+import {BsBook, BsCheckCircle, BsChevronLeft, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle} from "react-icons/bs";
import {toast} from "react-toastify";
export const getServerSideProps = withIronSessionSsr(async ({req, res, params}) => {
@@ -66,9 +69,9 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res, params})
},
};
- const users = await getEntitiesUsers(entityIDS);
- const entities = await getEntitiesWithRoles(entityIDS);
- const groups = await getGroupsByEntities(entityIDS);
+ const users = await (checkAccess(user, ["developer", "admin"]) ? getUsers() : getEntitiesUsers(entityIDS));
+ const entities = await (checkAccess(user, ["developer", "admin"]) ? getEntitiesWithRoles() : getEntitiesWithRoles(entityIDS));
+ const groups = await (checkAccess(user, ["developer", "admin"]) ? getGroups() : getGroupsByEntities(entityIDS));
return {props: serialize({user, users, entities, assignment, groups})};
}, sessionOptions);
@@ -209,141 +212,127 @@ export default function AssignmentsPage({assignment, user, users, entities, grou
};
return (
-
-
-
- toggleModule("reading") : undefined}
- className={clsx(
- "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
- selectedModules.includes("reading") ? "border-mti-purple-light" : "border-mti-gray-platinum",
- )}>
-
-
-
-
Reading
- {!selectedModules.includes("reading") && !selectedModules.includes("level") && (
-
- )}
- {selectedModules.includes("level") &&
}
- {selectedModules.includes("reading") &&
}
+ <>
+
+
Edit {assignment.name} | EnCoach
+
+
+
+
+
+
+
+
+
+
+
Edit {assignment.name}
-
toggleModule("listening") : undefined}
- className={clsx(
- "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
- selectedModules.includes("listening") ? "border-mti-purple-light" : "border-mti-gray-platinum",
- )}>
-
-
-
-
Listening
- {!selectedModules.includes("listening") && !selectedModules.includes("level") && (
-
- )}
- {selectedModules.includes("level") &&
}
- {selectedModules.includes("listening") &&
}
-
-
toggleModule("level")
- : undefined
- }
- className={clsx(
- "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
- selectedModules.includes("level") ? "border-mti-purple-light" : "border-mti-gray-platinum",
- )}>
-
-
-
-
Level
- {!selectedModules.includes("level") && selectedModules.length === 0 && (
-
- )}
- {!selectedModules.includes("level") && selectedModules.length > 0 &&
}
- {selectedModules.includes("level") &&
}
-
-
toggleModule("writing") : undefined}
- className={clsx(
- "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
- selectedModules.includes("writing") ? "border-mti-purple-light" : "border-mti-gray-platinum",
- )}>
-
-
-
-
Writing
- {!selectedModules.includes("writing") && !selectedModules.includes("level") && (
-
- )}
- {selectedModules.includes("level") &&
}
- {selectedModules.includes("writing") &&
}
-
-
toggleModule("speaking") : undefined}
- className={clsx(
- "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
- selectedModules.includes("speaking") ? "border-mti-purple-light" : "border-mti-gray-platinum",
- )}>
-
-
-
-
Speaking
- {!selectedModules.includes("speaking") && !selectedModules.includes("level") && (
-
- )}
- {selectedModules.includes("level") &&
}
- {selectedModules.includes("speaking") &&
}
-
-
-
-
- setName(e)} defaultValue={name} label="Assignment Name" required />
-
+
+
+ toggleModule("reading") : undefined}
+ className={clsx(
+ "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
+ selectedModules.includes("reading") ? "border-mti-purple-light" : "border-mti-gray-platinum",
+ )}>
+
+
+
+
Reading
+ {!selectedModules.includes("reading") && !selectedModules.includes("level") && (
+
+ )}
+ {selectedModules.includes("level") &&
}
+ {selectedModules.includes("reading") &&
}
+
+ toggleModule("listening") : undefined}
+ className={clsx(
+ "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
+ selectedModules.includes("listening") ? "border-mti-purple-light" : "border-mti-gray-platinum",
+ )}>
+
+
+
+
Listening
+ {!selectedModules.includes("listening") && !selectedModules.includes("level") && (
+
+ )}
+ {selectedModules.includes("level") &&
}
+ {selectedModules.includes("listening") &&
}
+
+ toggleModule("level")
+ : undefined
+ }
+ className={clsx(
+ "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
+ selectedModules.includes("level") ? "border-mti-purple-light" : "border-mti-gray-platinum",
+ )}>
+
+
+
+
Level
+ {!selectedModules.includes("level") && selectedModules.length === 0 && (
+
+ )}
+ {!selectedModules.includes("level") && selectedModules.length > 0 &&
}
+ {selectedModules.includes("level") &&
}
+
+ toggleModule("writing") : undefined}
+ className={clsx(
+ "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
+ selectedModules.includes("writing") ? "border-mti-purple-light" : "border-mti-gray-platinum",
+ )}>
+
+
+
+
Writing
+ {!selectedModules.includes("writing") && !selectedModules.includes("level") && (
+
+ )}
+ {selectedModules.includes("level") &&
}
+ {selectedModules.includes("writing") &&
}
+
+ toggleModule("speaking") : undefined}
+ className={clsx(
+ "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
+ selectedModules.includes("speaking") ? "border-mti-purple-light" : "border-mti-gray-platinum",
+ )}>
+
+
+
+
Speaking
+ {!selectedModules.includes("speaking") && !selectedModules.includes("level") && (
+
+ )}
+ {selectedModules.includes("level") &&
}
+ {selectedModules.includes("speaking") &&
}
+
+
-
-
-
-
moment(date).isSameOrAfter(new Date())}
- dateFormat="dd/MM/yyyy HH:mm"
- selected={startDate}
- showTimeSelect
- onChange={(date) => setStartDate(date)}
+
+ setName(e)} defaultValue={name} label="Assignment Name" required />
+
-
-
- moment(date).isAfter(startDate)}
- dateFormat="dd/MM/yyyy HH:mm"
- selected={endDate}
- showTimeSelect
- onChange={(date) => setEndDate(date)}
- />
-
- {autoStart && (
+
+
-
+
moment(date).isSameOrAfter(new Date())}
dateFormat="dd/MM/yyyy HH:mm"
- selected={autoStartDate}
+ selected={startDate}
showTimeSelect
- onChange={(date) => setAutoStartDate(date)}
+ onChange={(date) => setStartDate(date)}
/>
- )}
-
-
- {selectedModules.includes("speaking") && (
-
-
-
- )}
-
- {selectedModules.length > 0 && (
-
-
- Random Exams
-
- {!useRandomExams && (
-
- {selectedModules.map((module) => (
-
-
-
- ))}
+
+
+ moment(date).isAfter(startDate)}
+ dateFormat="dd/MM/yyyy HH:mm"
+ selected={endDate}
+ showTimeSelect
+ onChange={(date) => setEndDate(date)}
+ />
+
+ {autoStart && (
+
+
+ moment(date).isSameOrAfter(new Date())}
+ dateFormat="dd/MM/yyyy HH:mm"
+ selected={autoStartDate}
+ showTimeSelect
+ onChange={(date) => setAutoStartDate(date)}
+ />
)}
- )}
-
- Assignees ({assignees.length} selected)
-
- {classrooms.map((g) => (
-
+ )}
-
- {renderStudentSearch()}
- {renderStudentPagination()}
-
+ {selectedModules.length > 0 && (
+
+
+ Random Exams
+
+ {!useRandomExams && (
+
+ {selectedModules.map((module) => (
+
+
+
+ ))}
+
+ )}
+
+ )}
-
- {studentRows.map((user) => (
-
toggleAssignee(user)}
- className={clsx(
- "p-4 flex flex-col gap-2 rounded-xl border cursor-pointer w-72",
- "transition ease-in-out duration-300",
- assignees.includes(user.id) ? "border-mti-purple" : "border-mti-gray-platinum",
- )}
- key={user.id}>
-
- {user.name}
- {user.email}
-
-
-
- Groups:{" "}
- {groups
- .filter((g) => g.participants.includes(user.id))
- .map((g) => g.name)
- .join(", ")}
-
-
- ))}
-
-
-
- {user.type !== "teacher" && (
-
- Teachers ({teachers.length} selected)
+
+ Assignees ({assignees.length} selected)
{classrooms.map((g) => (
- {renderTeacherSearch()}
- {renderTeacherPagination()}
+ {renderStudentSearch()}
+ {renderStudentPagination()}
- {teacherRows.map((user) => (
+ {studentRows.map((user) => (
toggleTeacher(user)}
+ onClick={() => toggleAssignee(user)}
className={clsx(
"p-4 flex flex-col gap-2 rounded-xl border cursor-pointer w-72",
"transition ease-in-out duration-300",
- teachers.includes(user.id) ? "border-mti-purple" : "border-mti-gray-platinum",
+ assignees.includes(user.id) ? "border-mti-purple" : "border-mti-gray-platinum",
)}
key={user.id}>
{user.name}
{user.email}
+
Groups:{" "}
{groups
@@ -530,65 +498,123 @@ export default function AssignmentsPage({assignment, user, users, entities, grou
))}
- )}
-
-
setVariant((prev) => (prev === "full" ? "partial" : "full"))}>
- Full length exams
-
-
setGenerateMultiple((d) => !d)}>
- Generate different exams
-
-
setReleased((d) => !d)}>
- Auto release results
-
-
setAutostart((d) => !d)}>
- Auto start exam
-
+ {user.type !== "teacher" && (
+
+ Teachers ({teachers.length} selected)
+
+ {classrooms.map((g) => (
+
+ ))}
+
+
+
+ {renderTeacherSearch()}
+ {renderTeacherPagination()}
+
+
+
+ {teacherRows.map((user) => (
+
toggleTeacher(user)}
+ className={clsx(
+ "p-4 flex flex-col gap-2 rounded-xl border cursor-pointer w-72",
+ "transition ease-in-out duration-300",
+ teachers.includes(user.id) ? "border-mti-purple" : "border-mti-gray-platinum",
+ )}
+ key={user.id}>
+
+ {user.name}
+ {user.email}
+
+
+ Groups:{" "}
+ {groups
+ .filter((g) => g.participants.includes(user.id))
+ .map((g) => g.name)
+ .join(", ")}
+
+
+ ))}
+
+
+ )}
+
+
+ setVariant((prev) => (prev === "full" ? "partial" : "full"))}>
+ Full length exams
+
+ setGenerateMultiple((d) => !d)}>
+ Generate different exams
+
+ setReleased((d) => !d)}>
+ Auto release results
+
+ setAutostart((d) => !d)}>
+ Auto start exam
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+ >
);
}
diff --git a/src/pages/assignments/creator/index.tsx b/src/pages/assignments/creator/index.tsx
index f5cc0711..12a6a7fe 100644
--- a/src/pages/assignments/creator/index.tsx
+++ b/src/pages/assignments/creator/index.tsx
@@ -4,6 +4,7 @@ import Checkbox from "@/components/Low/Checkbox";
import Input from "@/components/Low/Input";
import ProgressBar from "@/components/Low/ProgressBar";
import Select from "@/components/Low/Select";
+import Separator from "@/components/Low/Separator";
import useExams from "@/hooks/useExams";
import {useListSearch} from "@/hooks/useListSearch";
import usePagination from "@/hooks/usePagination";
@@ -15,20 +16,22 @@ import {Group, User} from "@/interfaces/user";
import {sessionOptions} from "@/lib/session";
import {mapBy, serialize} from "@/utils";
import {getEntitiesWithRoles} from "@/utils/entities.be";
-import {getGroupsByEntities} from "@/utils/groups.be";
+import {getGroups, getGroupsByEntities} from "@/utils/groups.be";
import {checkAccess} from "@/utils/permissions";
import {calculateAverageLevel} from "@/utils/score";
-import {getEntitiesUsers} from "@/utils/users.be";
+import {getEntitiesUsers, getUsers} from "@/utils/users.be";
import axios from "axios";
import clsx from "clsx";
import {withIronSessionSsr} from "iron-session/next";
import {capitalize} from "lodash";
import moment from "moment";
+import Head from "next/head";
+import Link from "next/link";
import {useRouter} from "next/router";
import {generate} from "random-words";
import {useEffect, useMemo, useState} from "react";
import ReactDatePicker from "react-datepicker";
-import {BsBook, BsCheckCircle, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle} from "react-icons/bs";
+import {BsBook, BsCheckCircle, BsChevronLeft, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle} from "react-icons/bs";
import {toast} from "react-toastify";
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
@@ -53,9 +56,9 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
const entityIDS = mapBy(user.entities, "id") || [];
- const users = await getEntitiesUsers(entityIDS);
- const entities = await getEntitiesWithRoles(entityIDS);
- const groups = await getGroupsByEntities(entityIDS);
+ const users = await (checkAccess(user, ["developer", "admin"]) ? getUsers() : getEntitiesUsers(entityIDS));
+ const entities = await (checkAccess(user, ["developer", "admin"]) ? getEntitiesWithRoles() : getEntitiesWithRoles(entityIDS));
+ const groups = await (checkAccess(user, ["developer", "admin"]) ? getGroups() : getGroupsByEntities(entityIDS));
return {props: serialize({user, users, entities, groups})};
}, sessionOptions);
@@ -169,141 +172,127 @@ export default function AssignmentsPage({user, users, groups, entities}: Props)
};
return (
-
-
-
- toggleModule("reading") : undefined}
- className={clsx(
- "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
- selectedModules.includes("reading") ? "border-mti-purple-light" : "border-mti-gray-platinum",
- )}>
-
-
-
-
Reading
- {!selectedModules.includes("reading") && !selectedModules.includes("level") && (
-
- )}
- {selectedModules.includes("level") &&
}
- {selectedModules.includes("reading") &&
}
+ <>
+
+
Create Assignment | EnCoach
+
+
+
+
+
+
+
+
+
+
+
Create Assignment
-
toggleModule("listening") : undefined}
- className={clsx(
- "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
- selectedModules.includes("listening") ? "border-mti-purple-light" : "border-mti-gray-platinum",
- )}>
-
-
-
-
Listening
- {!selectedModules.includes("listening") && !selectedModules.includes("level") && (
-
- )}
- {selectedModules.includes("level") &&
}
- {selectedModules.includes("listening") &&
}
-
-
toggleModule("level")
- : undefined
- }
- className={clsx(
- "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
- selectedModules.includes("level") ? "border-mti-purple-light" : "border-mti-gray-platinum",
- )}>
-
-
-
-
Level
- {!selectedModules.includes("level") && selectedModules.length === 0 && (
-
- )}
- {!selectedModules.includes("level") && selectedModules.length > 0 &&
}
- {selectedModules.includes("level") &&
}
-
-
toggleModule("writing") : undefined}
- className={clsx(
- "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
- selectedModules.includes("writing") ? "border-mti-purple-light" : "border-mti-gray-platinum",
- )}>
-
-
-
-
Writing
- {!selectedModules.includes("writing") && !selectedModules.includes("level") && (
-
- )}
- {selectedModules.includes("level") &&
}
- {selectedModules.includes("writing") &&
}
-
-
toggleModule("speaking") : undefined}
- className={clsx(
- "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
- selectedModules.includes("speaking") ? "border-mti-purple-light" : "border-mti-gray-platinum",
- )}>
-
-
-
-
Speaking
- {!selectedModules.includes("speaking") && !selectedModules.includes("level") && (
-
- )}
- {selectedModules.includes("level") &&
}
- {selectedModules.includes("speaking") &&
}
-
-
-
-
- setName(e)} defaultValue={name} label="Assignment Name" required />
-
+
+
+ toggleModule("reading") : undefined}
+ className={clsx(
+ "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
+ selectedModules.includes("reading") ? "border-mti-purple-light" : "border-mti-gray-platinum",
+ )}>
+
+
+
+
Reading
+ {!selectedModules.includes("reading") && !selectedModules.includes("level") && (
+
+ )}
+ {selectedModules.includes("level") &&
}
+ {selectedModules.includes("reading") &&
}
+
+ toggleModule("listening") : undefined}
+ className={clsx(
+ "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
+ selectedModules.includes("listening") ? "border-mti-purple-light" : "border-mti-gray-platinum",
+ )}>
+
+
+
+
Listening
+ {!selectedModules.includes("listening") && !selectedModules.includes("level") && (
+
+ )}
+ {selectedModules.includes("level") &&
}
+ {selectedModules.includes("listening") &&
}
+
+ toggleModule("level")
+ : undefined
+ }
+ className={clsx(
+ "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
+ selectedModules.includes("level") ? "border-mti-purple-light" : "border-mti-gray-platinum",
+ )}>
+
+
+
+
Level
+ {!selectedModules.includes("level") && selectedModules.length === 0 && (
+
+ )}
+ {!selectedModules.includes("level") && selectedModules.length > 0 &&
}
+ {selectedModules.includes("level") &&
}
+
+ toggleModule("writing") : undefined}
+ className={clsx(
+ "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
+ selectedModules.includes("writing") ? "border-mti-purple-light" : "border-mti-gray-platinum",
+ )}>
+
+
+
+
Writing
+ {!selectedModules.includes("writing") && !selectedModules.includes("level") && (
+
+ )}
+ {selectedModules.includes("level") &&
}
+ {selectedModules.includes("writing") &&
}
+
+ toggleModule("speaking") : undefined}
+ className={clsx(
+ "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer",
+ selectedModules.includes("speaking") ? "border-mti-purple-light" : "border-mti-gray-platinum",
+ )}>
+
+
+
+
Speaking
+ {!selectedModules.includes("speaking") && !selectedModules.includes("level") && (
+
+ )}
+ {selectedModules.includes("level") &&
}
+ {selectedModules.includes("speaking") &&
}
+
+
-
-
-
-
moment(date).isSameOrAfter(new Date())}
- dateFormat="dd/MM/yyyy HH:mm"
- selected={startDate}
- showTimeSelect
- onChange={(date) => setStartDate(date)}
+
+ setName(e)} defaultValue={name} label="Assignment Name" required />
+
-
-
- moment(date).isAfter(startDate)}
- dateFormat="dd/MM/yyyy HH:mm"
- selected={endDate}
- showTimeSelect
- onChange={(date) => setEndDate(date)}
- />
-
- {autoStart && (
+
+
-
+
moment(date).isSameOrAfter(new Date())}
dateFormat="dd/MM/yyyy HH:mm"
- selected={autoStartDate}
+ selected={startDate}
showTimeSelect
- onChange={(date) => setAutoStartDate(date)}
+ onChange={(date) => setStartDate(date)}
/>
- )}
-
-
- {selectedModules.includes("speaking") && (
-
-
-
- )}
-
- {selectedModules.length > 0 && (
-
-
- Random Exams
-
- {!useRandomExams && (
-
- {selectedModules.map((module) => (
-
-
-
- ))}
+
+
+ moment(date).isAfter(startDate)}
+ dateFormat="dd/MM/yyyy HH:mm"
+ selected={endDate}
+ showTimeSelect
+ onChange={(date) => setEndDate(date)}
+ />
+
+ {autoStart && (
+
+
+ moment(date).isSameOrAfter(new Date())}
+ dateFormat="dd/MM/yyyy HH:mm"
+ selected={autoStartDate}
+ showTimeSelect
+ onChange={(date) => setAutoStartDate(date)}
+ />
)}
- )}
-
- Assignees ({assignees.length} selected)
-
- {classrooms.map((g) => (
-
+ )}
-
- {renderStudentSearch()}
- {renderStudentPagination()}
-
+ {selectedModules.length > 0 && (
+
+
+ Random Exams
+
+ {!useRandomExams && (
+
+ {selectedModules.map((module) => (
+
+
+
+ ))}
+
+ )}
+
+ )}
-
- {studentRows.map((user) => (
-
toggleAssignee(user)}
- className={clsx(
- "p-4 flex flex-col gap-2 rounded-xl border cursor-pointer w-72",
- "transition ease-in-out duration-300",
- assignees.includes(user.id) ? "border-mti-purple" : "border-mti-gray-platinum",
- )}
- key={user.id}>
-
- {user.name}
- {user.email}
-
-
-
- Groups:{" "}
- {groups
- .filter((g) => g.participants.includes(user.id))
- .map((g) => g.name)
- .join(", ")}
-
-
- ))}
-
-
-
- {user.type !== "teacher" && (
-
- Teachers ({teachers.length} selected)
+
+ Assignees ({assignees.length} selected)
{classrooms.map((g) => (
- {renderTeacherSearch()}
- {renderTeacherPagination()}
+ {renderStudentSearch()}
+ {renderStudentPagination()}
- {teacherRows.map((user) => (
+ {studentRows.map((user) => (
toggleTeacher(user)}
+ onClick={() => toggleAssignee(user)}
className={clsx(
"p-4 flex flex-col gap-2 rounded-xl border cursor-pointer w-72",
"transition ease-in-out duration-300",
- teachers.includes(user.id) ? "border-mti-purple" : "border-mti-gray-platinum",
+ assignees.includes(user.id) ? "border-mti-purple" : "border-mti-gray-platinum",
)}
key={user.id}>
{user.name}
{user.email}
+
Groups:{" "}
{groups
@@ -490,48 +458,106 @@ export default function AssignmentsPage({user, users, groups, entities}: Props)
))}
- )}
-
- setVariant((prev) => (prev === "full" ? "partial" : "full"))}>
- Full length exams
-
- setGenerateMultiple((d) => !d)}>
- Generate different exams
-
- setReleased((d) => !d)}>
- Auto release results
-
- setAutostart((d) => !d)}>
- Auto start exam
-
-
-
-
+ {user.type !== "teacher" && (
+
+ Teachers ({teachers.length} selected)
+
+ {classrooms.map((g) => (
+
+ ))}
+
-
+
+ {renderTeacherSearch()}
+ {renderTeacherPagination()}
+
+
+
+ {teacherRows.map((user) => (
+
toggleTeacher(user)}
+ className={clsx(
+ "p-4 flex flex-col gap-2 rounded-xl border cursor-pointer w-72",
+ "transition ease-in-out duration-300",
+ teachers.includes(user.id) ? "border-mti-purple" : "border-mti-gray-platinum",
+ )}
+ key={user.id}>
+
+ {user.name}
+ {user.email}
+
+
+ Groups:{" "}
+ {groups
+ .filter((g) => g.participants.includes(user.id))
+ .map((g) => g.name)
+ .join(", ")}
+
+
+ ))}
+
+
+ )}
+
+
+ setVariant((prev) => (prev === "full" ? "partial" : "full"))}>
+ Full length exams
+
+ setGenerateMultiple((d) => !d)}>
+ Generate different exams
+
+ setReleased((d) => !d)}>
+ Auto release results
+
+ setAutostart((d) => !d)}>
+ Auto start exam
+
+
+
+
+
+
+
-
-
+
+ >
);
}
diff --git a/src/pages/assignments/index.tsx b/src/pages/assignments/index.tsx
index c441e334..14eaacbb 100644
--- a/src/pages/assignments/index.tsx
+++ b/src/pages/assignments/index.tsx
@@ -1,6 +1,9 @@
import Layout from "@/components/High/Layout";
+import Separator from "@/components/Low/Separator";
import AssignmentCard from "@/dashboards/AssignmentCard";
import AssignmentView from "@/dashboards/AssignmentView";
+import {useListSearch} from "@/hooks/useListSearch";
+import usePagination from "@/hooks/usePagination";
import {Assignment} from "@/interfaces/results";
import {CorporateUser, Group, User} from "@/interfaces/user";
import {sessionOptions} from "@/lib/session";
@@ -13,13 +16,14 @@ import {
pastAssignmentFilter,
startHasExpiredAssignmentFilter,
} from "@/utils/assignments";
-import {getEntitiesAssignments} from "@/utils/assignments.be";
+import {getAssignments, getEntitiesAssignments} from "@/utils/assignments.be";
import {getEntitiesWithRoles} from "@/utils/entities.be";
-import {getGroupsByEntities} from "@/utils/groups.be";
+import {getGroups, getGroupsByEntities} from "@/utils/groups.be";
import {checkAccess} from "@/utils/permissions";
-import {getEntitiesUsers} from "@/utils/users.be";
+import {getEntitiesUsers, getUsers} from "@/utils/users.be";
import {withIronSessionSsr} from "iron-session/next";
import {groupBy} from "lodash";
+import Head from "next/head";
import Link from "next/link";
import {useRouter} from "next/router";
import {useMemo, useState} from "react";
@@ -47,14 +51,16 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
const entityIDS = mapBy(user.entities, "id") || [];
- const users = await getEntitiesUsers(entityIDS);
- const entities = await getEntitiesWithRoles(entityIDS);
- const assignments = await getEntitiesAssignments(entityIDS);
- const groups = await getGroupsByEntities(entityIDS);
+ const users = await (checkAccess(user, ["developer", "admin"]) ? getUsers() : getEntitiesUsers(entityIDS));
+ const entities = await (checkAccess(user, ["developer", "admin"]) ? getEntitiesWithRoles() : getEntitiesWithRoles(entityIDS));
+ const assignments = await (checkAccess(user, ["developer", "admin"]) ? getAssignments() : getEntitiesAssignments(entityIDS));
+ const groups = await (checkAccess(user, ["developer", "admin"]) ? getGroups() : getGroupsByEntities(entityIDS));
return {props: serialize({user, users, entities, assignments, groups})};
}, sessionOptions);
+const SEARCH_FIELDS = [["name"]];
+
interface Props {
assignments: Assignment[];
corporateAssignments?: ({corporate?: CorporateUser} & Assignment)[];
@@ -64,124 +70,159 @@ interface Props {
}
export default function AssignmentsPage({assignments, corporateAssignments, user, users, groups}: Props) {
- const [selectedAssignment, setSelectedAssignment] = useState();
- const [isCreatingAssignment, setIsCreatingAssignment] = useState(false);
-
- const displayAssignmentView = useMemo(() => !!selectedAssignment && !isCreatingAssignment, [selectedAssignment, isCreatingAssignment]);
-
- const assignmentsPastExpiredStart = assignments.filter(startHasExpiredAssignmentFilter);
+ const activeAssignments = useMemo(() => assignments.filter(activeAssignmentFilter), [assignments]);
+ const plannedAssignments = useMemo(() => assignments.filter(futureAssignmentFilter), [assignments]);
+ const pastAssignments = useMemo(() => assignments.filter(pastAssignmentFilter), [assignments]);
+ const startExpiredAssignments = useMemo(() => assignments.filter(startHasExpiredAssignmentFilter), [assignments]);
+ const archivedAssignments = useMemo(() => assignments.filter(archivedAssignmentFilter), [assignments]);
const router = useRouter();
+ const {rows: activeRows, renderSearch: renderActive} = useListSearch(SEARCH_FIELDS, activeAssignments);
+ const {rows: plannedRows, renderSearch: renderPlanned} = useListSearch(SEARCH_FIELDS, plannedAssignments);
+ const {rows: pastRows, renderSearch: renderPast} = useListSearch(SEARCH_FIELDS, pastAssignments);
+ const {rows: expiredRows, renderSearch: renderExpired} = useListSearch(SEARCH_FIELDS, startExpiredAssignments);
+ const {rows: archivedRows, renderSearch: renderArchived} = useListSearch(SEARCH_FIELDS, archivedAssignments);
+
+ const {items: activeItems, renderMinimal: paginationActive} = usePagination(activeRows, 16);
+ const {items: plannedItems, renderMinimal: paginationPlanned} = usePagination(plannedRows, 16);
+ const {items: pastItems, renderMinimal: paginationPast} = usePagination(pastRows, 16);
+ const {items: expiredItems, renderMinimal: paginationExpired} = usePagination(expiredRows, 16);
+ const {items: archivedItems, renderMinimal: paginationArchived} = usePagination(archivedRows, 16);
+
return (
-
- {displayAssignmentView && (
- {
- setSelectedAssignment(undefined);
- setIsCreatingAssignment(false);
- }}
- assignment={selectedAssignment}
+ <>
+
+ Assignments | EnCoach
+
- )}
-
-
-
-
-
-
Assignments
+
+
+
+
+
+
+
+
+
+
Assignments
+
+
-
-
-
Active Assignments Status
-
-
- Total: {assignments.filter(activeAssignmentFilter).reduce((acc, curr) => acc + curr.results.length, 0)}/
- {assignments.filter(activeAssignmentFilter).reduce((acc, curr) => curr.exams.length + acc, 0)}
-
- {Object.keys(groupBy(corporateAssignments, (x) => x.corporate?.id)).map((x) => (
-
- {getUserCompanyName(users.find((u) => u.id === x)!, users, groups)}:
-
- {groupBy(corporateAssignments, (x) => x.corporate?.id)[x].reduce((acc, curr) => curr.results.length + acc, 0)}/
- {groupBy(corporateAssignments, (x) => x.corporate?.id)[x].reduce((acc, curr) => curr.exams.length + acc, 0)}
-
-
- ))}
+
+
Active Assignments Status
+
+
+ Total: {activeAssignments.reduce((acc, curr) => acc + curr.results.length, 0)}/
+ {activeAssignments.reduce((acc, curr) => curr.exams.length + acc, 0)}
+
+ {Object.keys(groupBy(corporateAssignments, (x) => x.corporate?.id)).map((x) => (
+
+ {getUserCompanyName(users.find((u) => u.id === x)!, users, groups)}:
+
+ {groupBy(corporateAssignments, (x) => x.corporate?.id)[x].reduce((acc, curr) => curr.results.length + acc, 0)}/
+ {groupBy(corporateAssignments, (x) => x.corporate?.id)[x].reduce((acc, curr) => curr.exams.length + acc, 0)}
+
+
+ ))}
+
-
-
- Active Assignments ({assignments.filter(activeAssignmentFilter).length})
-
- {assignments.filter(activeAssignmentFilter).map((a) => (
-
setSelectedAssignment(a)} key={a.id} />
- ))}
-
-
-
- Planned Assignments ({assignments.filter(futureAssignmentFilter).length})
-
-
-
-
New Assignment
-
- {assignments.filter(futureAssignmentFilter).map((a) => (
-
router.push(`/assignments/creator/${a.id}`)} key={a.id} />
- ))}
-
-
-
- Past Assignments ({assignments.filter(pastAssignmentFilter).length})
-
- {assignments.filter(pastAssignmentFilter).map((a) => (
-
setSelectedAssignment(a)}
- key={a.id}
- allowDownload
- allowArchive
- allowExcelDownload
- />
- ))}
-
-
-
- Assignments start expired ({assignmentsPastExpiredStart.length})
-
- {assignments.filter(startHasExpiredAssignmentFilter).map((a) => (
-
setSelectedAssignment(a)}
- key={a.id}
- allowDownload
- allowArchive
- allowExcelDownload
- />
- ))}
-
-
-
- Archived Assignments ({assignments.filter(archivedAssignmentFilter).length})
-
- {assignments.filter(archivedAssignmentFilter).map((a) => (
-
setSelectedAssignment(a)}
- key={a.id}
- allowDownload
- allowUnarchive
- allowExcelDownload
- />
- ))}
-
-
-
+
+
+ Active Assignments ({activeAssignments.length})
+
+ {renderActive()}
+ {paginationActive()}
+
+
+ {activeItems.map((a) => (
+
router.push(`/assignments/${a.id}`)} key={a.id} />
+ ))}
+
+
+
+
+ Planned Assignments ({plannedAssignments.length})
+
+ {renderPlanned()}
+ {paginationPlanned()}
+
+
+
+
+
New Assignment
+
+ {plannedItems.map((a) => (
+
router.push(`/assignments/creator/${a.id}`)} key={a.id} />
+ ))}
+
+
+
+
+ Past Assignments ({pastAssignments.length})
+
+ {renderPast()}
+ {paginationPast()}
+
+
+ {pastItems.map((a) => (
+
router.push(`/assignments/${a.id}`)}
+ key={a.id}
+ allowDownload
+ allowArchive
+ allowExcelDownload
+ />
+ ))}
+
+
+
+ Assignments start expired ({startExpiredAssignments.length})
+
+ {renderExpired()}
+ {paginationExpired()}
+
+
+ {expiredItems.map((a) => (
+
router.push(`/assignments/${a.id}`)}
+ key={a.id}
+ allowDownload
+ allowArchive
+ allowExcelDownload
+ />
+ ))}
+
+
+
+ Archived Assignments ({archivedAssignments.length})
+
+ {renderArchived()}
+ {paginationArchived()}
+
+
+ {archivedItems.map((a) => (
+
router.push(`/assignments/${a.id}`)}
+ key={a.id}
+ allowDownload
+ allowUnarchive
+ allowExcelDownload
+ />
+ ))}
+
+
+
+ >
);
}
diff --git a/src/pages/classrooms/index.tsx b/src/pages/classrooms/index.tsx
index b88bb261..30120e96 100644
--- a/src/pages/classrooms/index.tsx
+++ b/src/pages/classrooms/index.tsx
@@ -96,7 +96,7 @@ export default function Home({user, groups}: Props) {
return (
<>
-
Groups | EnCoach
+
Classrooms | EnCoach
- {user && (
-
-
-
-
Classrooms
-
-
+
+
+
+
Classrooms
+
+
- list={groups} searchFields={SEARCH_FIELDS} renderCard={renderCard} firstCard={firstCard} />
-
-
- )}
+ list={groups} searchFields={SEARCH_FIELDS} renderCard={renderCard} firstCard={firstCard} />
+
+
>
);
}
diff --git a/src/pages/dashboard/admin/index.tsx b/src/pages/dashboard/admin/index.tsx
index dfe4ed7c..9b50f662 100644
--- a/src/pages/dashboard/admin/index.tsx
+++ b/src/pages/dashboard/admin/index.tsx
@@ -20,6 +20,7 @@ import {uniqBy} from "lodash";
import moment from "moment";
import Head from "next/head";
import Link from "next/link";
+import {useRouter} from "next/router";
import {useMemo} from "react";
import {
BsBank,
@@ -79,6 +80,8 @@ export default function Dashboard({user, users, entities, assignments, stats, gr
const corporates = useMemo(() => users.filter((u) => u.type === "corporate"), [users]);
const masterCorporates = useMemo(() => users.filter((u) => u.type === "mastercorporate"), [users]);
+ const router = useRouter();
+
const averageLevelCalculator = (studentStats: Stat[]) => {
const formattedStats = studentStats
.map((s) => ({
@@ -128,16 +131,46 @@ export default function Dashboard({user, users, entities, assignments, stats, gr
-
-
-
-
+ router.push("/lists/users?type=student")}
+ Icon={BsPersonFill}
+ label="Students"
+ value={students.length}
+ color="purple"
+ />
+ router.push("/lists/users?type=teacher")}
+ Icon={BsPencilSquare}
+ label="Teachers"
+ value={teachers.length}
+ color="purple"
+ />
+ router.push("/lists/users?type=corporate")}
+ label="Corporates"
+ value={corporates.length}
+ color="purple"
+ />
+ router.push("/lists/users?type=mastercorporate")}
+ label="Master Corporates"
+ value={masterCorporates.length}
+ color="purple"
+ />
- !a.archived).length} color="purple" />
+ router.push("/assignments")}
+ label="Assignments"
+ value={assignments.filter((a) => !a.archived).length}
+ color="purple"
+ />
diff --git a/src/pages/dashboard/corporate/index.tsx b/src/pages/dashboard/corporate/index.tsx
index da5466ad..4ce58192 100644
--- a/src/pages/dashboard/corporate/index.tsx
+++ b/src/pages/dashboard/corporate/index.tsx
@@ -1,229 +1,229 @@
/* eslint-disable @next/next/no-img-element */
import Layout from "@/components/High/Layout";
import IconCard from "@/dashboards/IconCard";
-import {Module} from "@/interfaces";
-import {EntityWithRoles} from "@/interfaces/entity";
-import {Assignment} from "@/interfaces/results";
-import {Group, Stat, User} from "@/interfaces/user";
-import {sessionOptions} from "@/lib/session";
-import {dateSorter, filterBy, mapBy, serialize} from "@/utils";
-import {getEntitiesAssignments} from "@/utils/assignments.be";
-import {getEntitiesWithRoles} from "@/utils/entities.be";
-import {getGroupsByEntities} from "@/utils/groups.be";
-import {checkAccess} from "@/utils/permissions";
-import {calculateAverageLevel, calculateBandScore} from "@/utils/score";
-import {groupByExam} from "@/utils/stats";
-import {getStatsByUsers} from "@/utils/stats.be";
-import {getEntitiesUsers} from "@/utils/users.be";
-import {withIronSessionSsr} from "iron-session/next";
-import {uniqBy} from "lodash";
+import { Module } from "@/interfaces";
+import { EntityWithRoles } from "@/interfaces/entity";
+import { Assignment } from "@/interfaces/results";
+import { Group, Stat, User } from "@/interfaces/user";
+import { sessionOptions } from "@/lib/session";
+import { dateSorter, filterBy, mapBy, serialize } from "@/utils";
+import { getEntitiesAssignments } from "@/utils/assignments.be";
+import { getEntitiesWithRoles } from "@/utils/entities.be";
+import { getGroupsByEntities } from "@/utils/groups.be";
+import { checkAccess } from "@/utils/permissions";
+import { calculateAverageLevel, calculateBandScore } from "@/utils/score";
+import { groupByExam } from "@/utils/stats";
+import { getStatsByUsers } from "@/utils/stats.be";
+import { getEntitiesUsers } from "@/utils/users.be";
+import { withIronSessionSsr } from "iron-session/next";
+import { uniqBy } from "lodash";
import moment from "moment";
import Head from "next/head";
-import Link from "next/link";
-import {useRouter} from "next/router";
-import {useMemo} from "react";
+import { useRouter } from "next/router";
+import { useMemo } from "react";
import {
- BsClipboard2Data,
- BsClock,
- BsEnvelopePaper,
- BsPaperclip,
- BsPencilSquare,
- BsPeople,
- BsPeopleFill,
- BsPersonFill,
- BsPersonFillGear,
+ BsClipboard2Data,
+ BsClock,
+ BsEnvelopePaper,
+ BsPaperclip,
+ BsPencilSquare,
+ BsPeople,
+ BsPeopleFill,
+ BsPersonFill,
+ BsPersonFillGear,
} from "react-icons/bs";
-import {ToastContainer} from "react-toastify";
+import { ToastContainer } from "react-toastify";
interface Props {
- user: User;
- users: User[];
- entities: EntityWithRoles[];
- assignments: Assignment[];
- stats: Stat[];
- groups: Group[];
+ user: User;
+ users: User[];
+ entities: EntityWithRoles[];
+ assignments: Assignment[];
+ stats: Stat[];
+ groups: Group[];
}
-export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
- const user = req.session.user as User | undefined;
+export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
+ const user = req.session.user as User | undefined;
- if (!user) {
- return {
- redirect: {
- destination: "/login",
- permanent: false,
- },
- };
- }
+ if (!user) {
+ return {
+ redirect: {
+ destination: "/login",
+ permanent: false,
+ },
+ };
+ }
- if (!checkAccess(user, ["admin", "developer", "corporate"]))
- return {
- redirect: {
- destination: "/dashboard",
- permanent: false,
- },
- };
+ if (!checkAccess(user, ["admin", "developer", "corporate"]))
+ return {
+ redirect: {
+ destination: "/dashboard",
+ permanent: false,
+ },
+ };
- const entityIDS = mapBy(user.entities, "id") || [];
+ const entityIDS = mapBy(user.entities, "id") || [];
- const users = await getEntitiesUsers(entityIDS);
- const entities = await getEntitiesWithRoles(entityIDS);
- const assignments = await getEntitiesAssignments(entityIDS);
- const stats = await getStatsByUsers(users.map((u) => u.id));
- const groups = await getGroupsByEntities(entityIDS);
+ const users = await getEntitiesUsers(entityIDS);
+ const entities = await getEntitiesWithRoles(entityIDS);
+ const assignments = await getEntitiesAssignments(entityIDS);
+ const stats = await getStatsByUsers(users.map((u) => u.id));
+ const groups = await getGroupsByEntities(entityIDS);
- return {props: serialize({user, users, entities, assignments, stats, groups})};
+ return { props: serialize({ user, users, entities, assignments, stats, groups }) };
}, sessionOptions);
-export default function Dashboard({user, users, entities, assignments, stats, groups}: Props) {
- const students = useMemo(() => users.filter((u) => u.type === "student"), [users]);
- const teachers = useMemo(() => users.filter((u) => u.type === "teacher"), [users]);
+export default function Dashboard({ user, users, entities, assignments, stats, groups }: Props) {
+ const students = useMemo(() => users.filter((u) => u.type === "student"), [users]);
+ const teachers = useMemo(() => users.filter((u) => u.type === "teacher"), [users]);
- const router = useRouter();
+ const router = useRouter();
- const averageLevelCalculator = (studentStats: Stat[]) => {
- const formattedStats = studentStats
- .map((s) => ({
- focus: students.find((u) => u.id === s.user)?.focus,
- score: s.score,
- module: s.module,
- }))
- .filter((f) => !!f.focus);
- const bandScores = formattedStats.map((s) => ({
- module: s.module,
- level: calculateBandScore(s.score.correct, s.score.total, s.module, s.focus!),
- }));
+ const averageLevelCalculator = (studentStats: Stat[]) => {
+ const formattedStats = studentStats
+ .map((s) => ({
+ focus: students.find((u) => u.id === s.user)?.focus,
+ score: s.score,
+ module: s.module,
+ }))
+ .filter((f) => !!f.focus);
+ const bandScores = formattedStats.map((s) => ({
+ module: s.module,
+ level: calculateBandScore(s.score.correct, s.score.total, s.module, s.focus!),
+ }));
- const levels: {[key in Module]: number} = {
- reading: 0,
- listening: 0,
- writing: 0,
- speaking: 0,
- level: 0,
- };
- bandScores.forEach((b) => (levels[b.module] += b.level));
+ const levels: { [key in Module]: number } = {
+ reading: 0,
+ listening: 0,
+ writing: 0,
+ speaking: 0,
+ level: 0,
+ };
+ bandScores.forEach((b) => (levels[b.module] += b.level));
- return calculateAverageLevel(levels);
- };
+ return calculateAverageLevel(levels);
+ };
- const UserDisplay = (displayUser: User) => (
-
-

-
- {displayUser.name}
- {displayUser.email}
-
-
- );
+ const UserDisplay = (displayUser: User) => (
+
+

+
+ {displayUser.name}
+ {displayUser.email}
+
+
+ );
- return (
- <>
-
- EnCoach
-
-
-
-
-
-
-
- {entities.length > 0 && (
-
- {mapBy(entities, "label")?.join(", ")}
-
- )}
-
- router.push("/lists/users?type=student")}
- Icon={BsPersonFill}
- label="Students"
- value={students.length}
- color="purple"
- />
- router.push("/lists/users?type=teacher")}
- Icon={BsPencilSquare}
- label="Teachers"
- value={teachers.length}
- color="purple"
- />
- router.push("/classrooms")}
- Icon={BsPeople}
- label="Classrooms"
- value={groups.length}
- color="purple"
- />
-
-
-
-
-
- !a.archived).length}
- color="purple"
- />
-
-
+ return (
+ <>
+
+ EnCoach
+
+
+
+
+
+
+
+ {entities.length > 0 && (
+
+ {mapBy(entities, "label")?.join(", ")}
+
+ )}
+
+ router.push("/lists/users?type=student")}
+ Icon={BsPersonFill}
+ label="Students"
+ value={students.length}
+ color="purple"
+ />
+ router.push("/lists/users?type=teacher")}
+ Icon={BsPencilSquare}
+ label="Teachers"
+ value={teachers.length}
+ color="purple"
+ />
+ router.push("/classrooms")}
+ Icon={BsPeople}
+ label="Classrooms"
+ value={groups.length}
+ color="purple"
+ />
+
+
+
+
+
+ router.push("/assignments")}
+ label="Assignments"
+ value={assignments.filter((a) => !a.archived).length}
+ color="purple"
+ />
+
+
-
-
-
Latest students
-
- {students
- .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
- .map((x) => (
-
- ))}
-
-
-
-
Latest teachers
-
- {teachers
- .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
- .map((x) => (
-
- ))}
-
-
-
-
Highest level students
-
- {students
- .sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))
- .map((x) => (
-
- ))}
-
-
-
-
Highest exam count students
-
- {students
- .sort(
- (a, b) =>
- Object.keys(groupByExam(filterBy(stats, "user", b))).length -
- Object.keys(groupByExam(filterBy(stats, "user", a))).length,
- )
- .map((x) => (
-
- ))}
-
-
-
-
- >
- );
+
+
+
Latest students
+
+ {students
+ .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
+ .map((x) => (
+
+ ))}
+
+
+
+
Latest teachers
+
+ {teachers
+ .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
+ .map((x) => (
+
+ ))}
+
+
+
+
Highest level students
+
+ {students
+ .sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))
+ .map((x) => (
+
+ ))}
+
+
+
+
Highest exam count students
+
+ {students
+ .sort(
+ (a, b) =>
+ Object.keys(groupByExam(filterBy(stats, "user", b))).length -
+ Object.keys(groupByExam(filterBy(stats, "user", a))).length,
+ )
+ .map((x) => (
+
+ ))}
+
+
+
+
+ >
+ );
}
diff --git a/src/pages/dashboard/developer/index.tsx b/src/pages/dashboard/developer/index.tsx
index 73569f0d..9b50f662 100644
--- a/src/pages/dashboard/developer/index.tsx
+++ b/src/pages/dashboard/developer/index.tsx
@@ -20,6 +20,7 @@ import {uniqBy} from "lodash";
import moment from "moment";
import Head from "next/head";
import Link from "next/link";
+import {useRouter} from "next/router";
import {useMemo} from "react";
import {
BsBank,
@@ -56,7 +57,7 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
};
}
- if (!checkAccess(user, ["developer"]))
+ if (!checkAccess(user, ["admin", "developer"]))
return {
redirect: {
destination: "/dashboard",
@@ -79,6 +80,8 @@ export default function Dashboard({user, users, entities, assignments, stats, gr
const corporates = useMemo(() => users.filter((u) => u.type === "corporate"), [users]);
const masterCorporates = useMemo(() => users.filter((u) => u.type === "mastercorporate"), [users]);
+ const router = useRouter();
+
const averageLevelCalculator = (studentStats: Stat[]) => {
const formattedStats = studentStats
.map((s) => ({
@@ -128,16 +131,46 @@ export default function Dashboard({user, users, entities, assignments, stats, gr
-
-
-
-
+ router.push("/lists/users?type=student")}
+ Icon={BsPersonFill}
+ label="Students"
+ value={students.length}
+ color="purple"
+ />
+ router.push("/lists/users?type=teacher")}
+ Icon={BsPencilSquare}
+ label="Teachers"
+ value={teachers.length}
+ color="purple"
+ />
+ router.push("/lists/users?type=corporate")}
+ label="Corporates"
+ value={corporates.length}
+ color="purple"
+ />
+ router.push("/lists/users?type=mastercorporate")}
+ label="Master Corporates"
+ value={masterCorporates.length}
+ color="purple"
+ />
- !a.archived).length} color="purple" />
+ router.push("/assignments")}
+ label="Assignments"
+ value={assignments.filter((a) => !a.archived).length}
+ color="purple"
+ />
diff --git a/src/pages/dashboard/mastercorporate/index.tsx b/src/pages/dashboard/mastercorporate/index.tsx
index 314e713d..df32c467 100644
--- a/src/pages/dashboard/mastercorporate/index.tsx
+++ b/src/pages/dashboard/mastercorporate/index.tsx
@@ -1,201 +1,220 @@
/* eslint-disable @next/next/no-img-element */
import Layout from "@/components/High/Layout";
import IconCard from "@/dashboards/IconCard";
-import {Module} from "@/interfaces";
-import {EntityWithRoles} from "@/interfaces/entity";
-import {Assignment} from "@/interfaces/results";
-import {Group, Stat, User} from "@/interfaces/user";
-import {sessionOptions} from "@/lib/session";
-import {dateSorter, filterBy, mapBy, serialize} from "@/utils";
-import {getEntitiesAssignments} from "@/utils/assignments.be";
-import {getEntitiesWithRoles} from "@/utils/entities.be";
-import {getGroupsByEntities} from "@/utils/groups.be";
-import {checkAccess} from "@/utils/permissions";
-import {calculateAverageLevel, calculateBandScore} from "@/utils/score";
-import {groupByExam} from "@/utils/stats";
-import {getStatsByUsers} from "@/utils/stats.be";
-import {getEntitiesUsers} from "@/utils/users.be";
-import {withIronSessionSsr} from "iron-session/next";
-import {uniqBy} from "lodash";
+import { Module } from "@/interfaces";
+import { EntityWithRoles } from "@/interfaces/entity";
+import { Assignment } from "@/interfaces/results";
+import { Group, Stat, User } from "@/interfaces/user";
+import { sessionOptions } from "@/lib/session";
+import { dateSorter, filterBy, mapBy, serialize } from "@/utils";
+import { getEntitiesAssignments } from "@/utils/assignments.be";
+import { getEntitiesWithRoles } from "@/utils/entities.be";
+import { getGroupsByEntities } from "@/utils/groups.be";
+import { checkAccess } from "@/utils/permissions";
+import { calculateAverageLevel, calculateBandScore } from "@/utils/score";
+import { groupByExam } from "@/utils/stats";
+import { getStatsByUsers } from "@/utils/stats.be";
+import { getEntitiesUsers } from "@/utils/users.be";
+import { withIronSessionSsr } from "iron-session/next";
+import { uniqBy } from "lodash";
import moment from "moment";
import Head from "next/head";
import Link from "next/link";
-import {useRouter} from "next/router";
-import {useMemo} from "react";
+import { useRouter } from "next/router";
+import { useMemo } from "react";
import {
- BsBank,
- BsClipboard2Data,
- BsClock,
- BsEnvelopePaper,
- BsPaperclip,
- BsPencilSquare,
- BsPeople,
- BsPeopleFill,
- BsPersonFill,
- BsPersonFillGear,
+ BsBank,
+ BsClipboard2Data,
+ BsClock,
+ BsEnvelopePaper,
+ BsPaperclip,
+ BsPencilSquare,
+ BsPeople,
+ BsPeopleFill,
+ BsPersonFill,
+ BsPersonFillGear,
} from "react-icons/bs";
-import {ToastContainer} from "react-toastify";
+import { ToastContainer } from "react-toastify";
interface Props {
- user: User;
- users: User[];
- entities: EntityWithRoles[];
- assignments: Assignment[];
- stats: Stat[];
- groups: Group[];
+ user: User;
+ users: User[];
+ entities: EntityWithRoles[];
+ assignments: Assignment[];
+ stats: Stat[];
+ groups: Group[];
}
-export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
- const user = req.session.user as User | undefined;
+export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
+ const user = req.session.user as User | undefined;
- if (!user) {
- return {
- redirect: {
- destination: "/login",
- permanent: false,
- },
- };
- }
+ if (!user) {
+ return {
+ redirect: {
+ destination: "/login",
+ permanent: false,
+ },
+ };
+ }
- if (!checkAccess(user, ["admin", "developer", "mastercorporate"]))
- return {
- redirect: {
- destination: "/dashboard",
- permanent: false,
- },
- };
+ if (!checkAccess(user, ["admin", "developer", "mastercorporate"]))
+ return {
+ redirect: {
+ destination: "/dashboard",
+ permanent: false,
+ },
+ };
- const entityIDS = mapBy(user.entities, "id") || [];
+ const entityIDS = mapBy(user.entities, "id") || [];
- const users = await getEntitiesUsers(entityIDS);
- const entities = await getEntitiesWithRoles(entityIDS);
- const assignments = await getEntitiesAssignments(entityIDS);
- const stats = await getStatsByUsers(users.map((u) => u.id));
- const groups = await getGroupsByEntities(entityIDS);
+ const users = await getEntitiesUsers(entityIDS);
+ const entities = await getEntitiesWithRoles(entityIDS);
+ const assignments = await getEntitiesAssignments(entityIDS);
+ const stats = await getStatsByUsers(users.map((u) => u.id));
+ const groups = await getGroupsByEntities(entityIDS);
- return {props: serialize({user, users, entities, assignments, stats, groups})};
+ return { props: serialize({ user, users, entities, assignments, stats, groups }) };
}, sessionOptions);
-export default function Dashboard({user, users, entities, assignments, stats, groups}: Props) {
- const students = useMemo(() => users.filter((u) => u.type === "student"), [users]);
- const teachers = useMemo(() => users.filter((u) => u.type === "teacher"), [users]);
- const corporates = useMemo(() => users.filter((u) => u.type === "corporate"), [users]);
+export default function Dashboard({ user, users, entities, assignments, stats, groups }: Props) {
+ const students = useMemo(() => users.filter((u) => u.type === "student"), [users]);
+ const teachers = useMemo(() => users.filter((u) => u.type === "teacher"), [users]);
+ const corporates = useMemo(() => users.filter((u) => u.type === "corporate"), [users]);
- const router = useRouter();
+ const router = useRouter();
- const averageLevelCalculator = (studentStats: Stat[]) => {
- const formattedStats = studentStats
- .map((s) => ({
- focus: students.find((u) => u.id === s.user)?.focus,
- score: s.score,
- module: s.module,
- }))
- .filter((f) => !!f.focus);
- const bandScores = formattedStats.map((s) => ({
- module: s.module,
- level: calculateBandScore(s.score.correct, s.score.total, s.module, s.focus!),
- }));
+ const averageLevelCalculator = (studentStats: Stat[]) => {
+ const formattedStats = studentStats
+ .map((s) => ({
+ focus: students.find((u) => u.id === s.user)?.focus,
+ score: s.score,
+ module: s.module,
+ }))
+ .filter((f) => !!f.focus);
+ const bandScores = formattedStats.map((s) => ({
+ module: s.module,
+ level: calculateBandScore(s.score.correct, s.score.total, s.module, s.focus!),
+ }));
- const levels: {[key in Module]: number} = {
- reading: 0,
- listening: 0,
- writing: 0,
- speaking: 0,
- level: 0,
- };
- bandScores.forEach((b) => (levels[b.module] += b.level));
+ const levels: { [key in Module]: number } = {
+ reading: 0,
+ listening: 0,
+ writing: 0,
+ speaking: 0,
+ level: 0,
+ };
+ bandScores.forEach((b) => (levels[b.module] += b.level));
- return calculateAverageLevel(levels);
- };
+ return calculateAverageLevel(levels);
+ };
- const UserDisplay = (displayUser: User) => (
-
-

-
- {displayUser.name}
- {displayUser.email}
-
-
- );
+ const UserDisplay = (displayUser: User) => (
+
+

+
+ {displayUser.name}
+ {displayUser.email}
+
+
+ );
- return (
- <>
-
- EnCoach
-
-
-
-
-
-
-
-
-
-
- router.push("/classrooms")} Icon={BsPeople} label="Classrooms" value={groups.length} color="purple" />
-
-
-
-
- !a.archived).length} color="purple" />
-
-
+ return (
+ <>
+
+ EnCoach
+
+
+
+
+
+
+
+ router.push("/lists/users?type=student")}
+ Icon={BsPersonFill}
+ label="Students"
+ value={students.length}
+ color="purple"
+ />
+ router.push("/lists/users?type=teacher")}
+ Icon={BsPencilSquare}
+ label="Teachers"
+ value={teachers.length}
+ color="purple"
+ />
+ router.push("/lists/users?type=corporate")} Icon={BsBank} label="Corporate Accounts" value={corporates.length} color="purple" />
+ router.push("/classrooms")} Icon={BsPeople} label="Classrooms" value={groups.length} color="purple" />
+
+
+
+
+ router.push("/assignments")}
+ label="Assignments"
+ value={assignments.filter((a) => !a.archived).length}
+ color="purple"
+ />
+
+
-
-
-
Latest students
-
- {students
- .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
- .map((x) => (
-
- ))}
-
-
-
-
Latest teachers
-
- {teachers
- .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
- .map((x) => (
-
- ))}
-
-
-
-
Highest level students
-
- {students
- .sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))
- .map((x) => (
-
- ))}
-
-
-
-
Highest exam count students
-
- {students
- .sort(
- (a, b) =>
- Object.keys(groupByExam(filterBy(stats, "user", b))).length -
- Object.keys(groupByExam(filterBy(stats, "user", a))).length,
- )
- .map((x) => (
-
- ))}
-
-
-
-
- >
- );
+
+
+
Latest students
+
+ {students
+ .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
+ .map((x) => (
+
+ ))}
+
+
+
+
Latest teachers
+
+ {teachers
+ .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
+ .map((x) => (
+
+ ))}
+
+
+
+
Highest level students
+
+ {students
+ .sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))
+ .map((x) => (
+
+ ))}
+
+
+
+
Highest exam count students
+
+ {students
+ .sort(
+ (a, b) =>
+ Object.keys(groupByExam(filterBy(stats, "user", b))).length -
+ Object.keys(groupByExam(filterBy(stats, "user", a))).length,
+ )
+ .map((x) => (
+
+ ))}
+
+
+
+
+ >
+ );
}
diff --git a/src/pages/dashboard/teacher/index.tsx b/src/pages/dashboard/teacher/index.tsx
index 713a10d2..dc163117 100644
--- a/src/pages/dashboard/teacher/index.tsx
+++ b/src/pages/dashboard/teacher/index.tsx
@@ -132,7 +132,13 @@ export default function Dashboard({user, users, entities, assignments, stats, gr
/>
- !a.archived).length} color="purple" />
+ router.push("/assignments")}
+ label="Assignments"
+ value={assignments.filter((a) => !a.archived).length}
+ color="purple"
+ />
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index cc872198..41caa641 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -1,40 +1,6 @@
-/* eslint-disable @next/next/no-img-element */
-import Head from "next/head";
-import Navbar from "@/components/Navbar";
-import {BsFileEarmarkText, BsPencil, BsStar, BsBook, BsHeadphones, BsPen, BsMegaphone} from "react-icons/bs";
-import {withIronSessionSsr} from "iron-session/next";
+import {User} from "@/interfaces/user";
import {sessionOptions} from "@/lib/session";
-import {useEffect, useState} from "react";
-import {averageScore, groupBySession, totalExams} from "@/utils/stats";
-import useUser from "@/hooks/useUser";
-import Diagnostic from "@/components/Diagnostic";
-import {ToastContainer} from "react-toastify";
-import {capitalize} from "lodash";
-import {Module} from "@/interfaces";
-import ProgressBar from "@/components/Low/ProgressBar";
-import Layout from "@/components/High/Layout";
-import {calculateAverageLevel} from "@/utils/score";
-import axios from "axios";
-import DemographicInformationInput from "@/components/DemographicInformationInput";
-import moment from "moment";
-import Link from "next/link";
-import {MODULE_ARRAY} from "@/utils/moduleUtils";
-import ProfileSummary from "@/components/ProfileSummary";
-import StudentDashboard from "@/dashboards/Student";
-import AdminDashboard from "@/dashboards/Admin";
-import CorporateDashboard from "@/dashboards/Corporate";
-import TeacherDashboard from "@/dashboards/Teacher";
-import AgentDashboard from "@/dashboards/Agent";
-import MasterCorporateDashboard from "@/dashboards/MasterCorporate";
-import PaymentDue from "./(status)/PaymentDue";
-import {useRouter} from "next/router";
-import {PayPalScriptProvider} from "@paypal/react-paypal-js";
-import {CorporateUser, MasterCorporateUser, Type, User, userTypes} from "@/interfaces/user";
-import Select from "react-select";
-import {USER_TYPE_LABELS} from "@/resources/user";
-import {checkAccess, getTypesOfUser} from "@/utils/permissions";
-import {getUserCorporate} from "@/utils/groups.be";
-import {getUsers} from "@/utils/users.be";
+import {withIronSessionSsr} from "iron-session/next";
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
const user = req.session.user as User | undefined;
@@ -48,158 +14,14 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
};
}
- const linkedCorporate = (await getUserCorporate(user.id)) || null;
-
return {
- props: {user, linkedCorporate},
+ redirect: {
+ destination: `/dashboard/${user.type}`,
+ permanent: false,
+ },
};
}, sessionOptions);
-interface Props {
- user: User;
- linkedCorporate?: CorporateUser | MasterCorporateUser;
-}
-
-export default function Home({user: propsUser, linkedCorporate}: Props) {
- const [user, setUser] = useState(propsUser);
- const [showDiagnostics, setShowDiagnostics] = useState(false);
- const [showDemographicInput, setShowDemographicInput] = useState(false);
- const [selectedScreen, setSelectedScreen] = useState
("admin");
-
- const {mutateUser} = useUser({redirectTo: "/login"});
- const router = useRouter();
-
- useEffect(() => {
- if (user) {
- // setShowDemographicInput(!user.demographicInformation || !user.demographicInformation.country || !user.demographicInformation.phone);
- setShowDiagnostics(user.isFirstLogin && user.type === "student");
- }
- }, [user]);
-
- const checkIfUserExpired = () => {
- const expirationDate = user!.subscriptionExpirationDate;
-
- if (expirationDate === null || expirationDate === undefined) return false;
- if (moment(expirationDate).isAfter(moment(new Date()))) return false;
-
- return true;
- };
-
- if (user && (user.status === "paymentDue" || user.status === "disabled" || checkIfUserExpired())) {
- return (
- <>
-
- EnCoach
-
-
-
-
- {user.status === "disabled" && (
-
-
- Your account has been disabled!
- Please contact an administrator if you believe this to be a mistake.
-
-
- )}
- {(user.status === "paymentDue" || checkIfUserExpired()) && }
- >
- );
- }
-
- if (user && showDemographicInput) {
- return (
- <>
-
- EnCoach
-
-
-
-
-
- {
- setUser(user);
- mutateUser(user);
- }}
- user={user}
- />
-
- >
- );
- }
-
- if (user && showDiagnostics) {
- return (
- <>
-
- EnCoach
-
-
-
-
-
- setShowDiagnostics(false)} />
-
- >
- );
- }
-
- return (
- <>
-
- EnCoach
-
-
-
-
-
- {user && (
-
- {checkAccess(user, ["student"]) && }
- {checkAccess(user, ["teacher"]) && }
- {checkAccess(user, ["corporate"]) && }
- {checkAccess(user, ["mastercorporate"]) && }
- {checkAccess(user, ["agent"]) && }
- {checkAccess(user, ["admin"]) && }
- {checkAccess(user, ["developer"]) && (
- <>
-
- )}
- >
- );
+export default function Dashboard() {
+ return ;
}
diff --git a/src/pages/v1/index.tsx b/src/pages/v1/index.tsx
new file mode 100644
index 00000000..e987ae68
--- /dev/null
+++ b/src/pages/v1/index.tsx
@@ -0,0 +1,205 @@
+/* eslint-disable @next/next/no-img-element */
+import Head from "next/head";
+import Navbar from "@/components/Navbar";
+import { BsFileEarmarkText, BsPencil, BsStar, BsBook, BsHeadphones, BsPen, BsMegaphone } from "react-icons/bs";
+import { withIronSessionSsr } from "iron-session/next";
+import { sessionOptions } from "@/lib/session";
+import { useEffect, useState } from "react";
+import { averageScore, groupBySession, totalExams } from "@/utils/stats";
+import useUser from "@/hooks/useUser";
+import Diagnostic from "@/components/Diagnostic";
+import { ToastContainer } from "react-toastify";
+import { capitalize } from "lodash";
+import { Module } from "@/interfaces";
+import ProgressBar from "@/components/Low/ProgressBar";
+import Layout from "@/components/High/Layout";
+import { calculateAverageLevel } from "@/utils/score";
+import axios from "axios";
+import DemographicInformationInput from "@/components/DemographicInformationInput";
+import moment from "moment";
+import Link from "next/link";
+import { MODULE_ARRAY } from "@/utils/moduleUtils";
+import ProfileSummary from "@/components/ProfileSummary";
+import StudentDashboard from "@/dashboards/Student";
+import AdminDashboard from "@/dashboards/Admin";
+import CorporateDashboard from "@/dashboards/Corporate";
+import TeacherDashboard from "@/dashboards/Teacher";
+import AgentDashboard from "@/dashboards/Agent";
+import MasterCorporateDashboard from "@/dashboards/MasterCorporate";
+import PaymentDue from "../(status)/PaymentDue";
+import { useRouter } from "next/router";
+import { PayPalScriptProvider } from "@paypal/react-paypal-js";
+import { CorporateUser, MasterCorporateUser, Type, User, userTypes } from "@/interfaces/user";
+import Select from "react-select";
+import { USER_TYPE_LABELS } from "@/resources/user";
+import { checkAccess, getTypesOfUser } from "@/utils/permissions";
+import { getUserCorporate } from "@/utils/groups.be";
+import { getUsers } from "@/utils/users.be";
+
+export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
+ const user = req.session.user as User | undefined;
+
+ if (!user) {
+ return {
+ redirect: {
+ destination: "/login",
+ permanent: false,
+ },
+ };
+ }
+
+ const linkedCorporate = (await getUserCorporate(user.id)) || null;
+
+ return {
+ props: { user, linkedCorporate },
+ };
+}, sessionOptions);
+
+interface Props {
+ user: User;
+ linkedCorporate?: CorporateUser | MasterCorporateUser;
+}
+
+export default function Home({ user: propsUser, linkedCorporate }: Props) {
+ const [user, setUser] = useState(propsUser);
+ const [showDiagnostics, setShowDiagnostics] = useState(false);
+ const [showDemographicInput, setShowDemographicInput] = useState(false);
+ const [selectedScreen, setSelectedScreen] = useState("admin");
+
+ const { mutateUser } = useUser({ redirectTo: "/login" });
+ const router = useRouter();
+
+ useEffect(() => {
+ if (user) {
+ // setShowDemographicInput(!user.demographicInformation || !user.demographicInformation.country || !user.demographicInformation.phone);
+ setShowDiagnostics(user.isFirstLogin && user.type === "student");
+ }
+ }, [user]);
+
+ const checkIfUserExpired = () => {
+ const expirationDate = user!.subscriptionExpirationDate;
+
+ if (expirationDate === null || expirationDate === undefined) return false;
+ if (moment(expirationDate).isAfter(moment(new Date()))) return false;
+
+ return true;
+ };
+
+ if (user && (user.status === "paymentDue" || user.status === "disabled" || checkIfUserExpired())) {
+ return (
+ <>
+
+ EnCoach
+
+
+
+
+ {user.status === "disabled" && (
+
+
+ Your account has been disabled!
+ Please contact an administrator if you believe this to be a mistake.
+
+
+ )}
+ {(user.status === "paymentDue" || checkIfUserExpired()) && }
+ >
+ );
+ }
+
+ if (user && showDemographicInput) {
+ return (
+ <>
+
+ EnCoach
+
+
+
+
+
+ {
+ setUser(user);
+ mutateUser(user);
+ }}
+ user={user}
+ />
+
+ >
+ );
+ }
+
+ if (user && showDiagnostics) {
+ return (
+ <>
+
+ EnCoach
+
+
+
+
+
+ setShowDiagnostics(false)} />
+
+ >
+ );
+ }
+
+ return (
+ <>
+
+ EnCoach
+
+
+
+
+
+ {user && (
+
+ {checkAccess(user, ["student"]) && }
+ {checkAccess(user, ["teacher"]) && }
+ {checkAccess(user, ["corporate"]) && }
+ {checkAccess(user, ["mastercorporate"]) && }
+ {checkAccess(user, ["agent"]) && }
+ {checkAccess(user, ["admin"]) && }
+ {checkAccess(user, ["developer"]) && (
+ <>
+
+ )}
+ >
+ );
+}