From b5200c88fc3a66a6ce722eaa707bd9f099ab97e1 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Thu, 3 Oct 2024 11:32:43 +0100 Subject: [PATCH] Updated the dashboard to the v2 version --- src/components/Sidebar.tsx | 450 ++++++------ src/dashboards/AssignmentCard.tsx | 13 +- src/pages/assignments/[id].tsx | 421 +++++++++++ src/pages/assignments/creator/[id].tsx | 658 +++++++++--------- src/pages/assignments/creator/index.tsx | 622 +++++++++-------- src/pages/assignments/index.tsx | 279 ++++---- src/pages/classrooms/index.tsx | 22 +- src/pages/dashboard/admin/index.tsx | 43 +- src/pages/dashboard/corporate/index.tsx | 406 +++++------ src/pages/dashboard/developer/index.tsx | 45 +- src/pages/dashboard/mastercorporate/index.tsx | 367 +++++----- src/pages/dashboard/teacher/index.tsx | 8 +- src/pages/index.tsx | 194 +----- src/pages/v1/index.tsx | 205 ++++++ 14 files changed, 2181 insertions(+), 1552 deletions(-) create mode 100644 src/pages/assignments/[id].tsx create mode 100644 src/pages/v1/index.tsx 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 && ( -
- {badge} -
- )} - - ); +const Nav = ({ Icon, label, path, keyPath, disabled = false, isMinimized = false, badge }: NavProps) => { + return ( + + + {!isMinimized && {label}} + {!!badge && badge > 0 && ( +
+ {badge} +
+ )} + + ); }; -export default function Sidebar({path, navDisabled = false, focusMode = false, user, onFocusLayerMouseEnter, className}: Props) { - const router = useRouter(); +export default function Sidebar({ path, navDisabled = false, focusMode = false, user, onFocusLayerMouseEnter, className }: Props) { + const router = useRouter(); - const [isMinimized, toggleMinimize] = usePreferencesStore((state) => [state.isSidebarMinimized, state.toggleSidebarMinimized]); + const [isMinimized, toggleMinimize] = usePreferencesStore((state) => [state.isSidebarMinimized, state.toggleSidebarMinimized]); - const {totalAssignedTickets} = useTicketsListener(user.id); - const {permissions} = usePermissions(user.id); + const { totalAssignedTickets } = useTicketsListener(user.id); + const { permissions } = usePermissions(user.id); - const logout = async () => { - axios.post("/api/logout").finally(() => { - setTimeout(() => router.reload(), 500); - }); - }; + const logout = async () => { + axios.post("/api/logout").finally(() => { + setTimeout(() => router.reload(), 500); + }); + }; - const disableNavigation = preventNavigation(navDisabled, focusMode); + const disableNavigation = preventNavigation(navDisabled, focusMode); - return ( -
-
-
-
-
+ return ( +
+
+
+
+
-
-
- {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 /> - setName(e)} defaultValue={name} label="Assignment Name" required /> + (value ? setInstructorGender(value.value as InstructorGender) : null)} - disabled={!selectedModules.includes("speaking") || !!assignment} - options={[ - {value: "male", label: "Male"}, - {value: "female", label: "Female"}, - {value: "varied", label: "Varied"}, - ]} - /> -
- )} - - {selectedModules.length > 0 && ( -
- - Random Exams - - {!useRandomExams && ( -
- {selectedModules.map((module) => ( -
- - g.participants.includes(u.id)).every((u) => assignees.includes(u.id)) && - "!bg-mti-purple-light !text-white", - )}> - {g.name} - - ))} -
+ onChange={(value) => (value ? setInstructorGender(value.value as InstructorGender) : null)} + disabled={!selectedModules.includes("speaking") || !!assignment} + options={[ + {value: "male", label: "Male"}, + {value: "female", label: "Female"}, + {value: "varied", label: "Varied"}, + ]} + /> +
+ )} -
- {renderStudentSearch()} - {renderStudentPagination()} -
+ {selectedModules.length > 0 && ( +
+ + Random Exams + + {!useRandomExams && ( +
+ {selectedModules.map((module) => ( +
+ + setName(e)} defaultValue={name} label="Assignment Name" required /> - setName(e)} defaultValue={name} label="Assignment Name" required /> + (value ? setInstructorGender(value.value as InstructorGender) : null)} - disabled={!selectedModules.includes("speaking")} - options={[ - {value: "male", label: "Male"}, - {value: "female", label: "Female"}, - {value: "varied", label: "Varied"}, - ]} - /> -
- )} - - {selectedModules.length > 0 && ( -
- - Random Exams - - {!useRandomExams && ( -
- {selectedModules.map((module) => ( -
- - g.participants.includes(u.id)).every((u) => assignees.includes(u.id)) && - "!bg-mti-purple-light !text-white", - )}> - {g.name} - - ))} -
+ onChange={(value) => (value ? setInstructorGender(value.value as InstructorGender) : null)} + disabled={!selectedModules.includes("speaking")} + options={[ + {value: "male", label: "Male"}, + {value: "female", label: "Female"}, + {value: "varied", label: "Varied"}, + ]} + /> +
+ )} -
- {renderStudentSearch()} - {renderStudentPagination()} -
+ {selectedModules.length > 0 && ( +
+ + Random Exams + + {!useRandomExams && ( +
+ {selectedModules.map((module) => ( +
+ + ({ - value: u, - label: USER_TYPE_LABELS[u], - }))} - value={{ - value: selectedScreen, - label: USER_TYPE_LABELS[selectedScreen], - }} - onChange={(value) => (value ? setSelectedScreen(value.value) : setSelectedScreen("admin"))} - /> - - {selectedScreen === "student" && } - {selectedScreen === "teacher" && } - {selectedScreen === "corporate" && ( - - )} - {selectedScreen === "mastercorporate" && } - {selectedScreen === "agent" && } - {selectedScreen === "admin" && } - - )} - - )} - - ); +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"]) && ( + <> +