Created a system to go directly to an assignment from a URL
This commit is contained in:
@@ -11,16 +11,18 @@ interface Props {
|
|||||||
className?: string;
|
className?: string;
|
||||||
navDisabled?: boolean;
|
navDisabled?: boolean;
|
||||||
focusMode?: boolean;
|
focusMode?: boolean;
|
||||||
|
hideSidebar?: boolean
|
||||||
bgColor?: string;
|
bgColor?: string;
|
||||||
onFocusLayerMouseEnter?: () => void;
|
onFocusLayerMouseEnter?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Layout({user, children, className, bgColor="bg-white", navDisabled = false, focusMode = false, onFocusLayerMouseEnter}: Props) {
|
export default function Layout({user, children, className, bgColor="bg-white", hideSidebar, navDisabled = false, focusMode = false, onFocusLayerMouseEnter}: Props) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className={clsx("w-full min-h-full h-screen flex flex-col bg-mti-gray-smoke relative")}>
|
<main className={clsx("w-full min-h-full h-screen flex flex-col bg-mti-gray-smoke relative")}>
|
||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
|
{!hideSidebar && (
|
||||||
<Navbar
|
<Navbar
|
||||||
path={router.pathname}
|
path={router.pathname}
|
||||||
user={user}
|
user={user}
|
||||||
@@ -28,7 +30,9 @@ export default function Layout({user, children, className, bgColor="bg-white", n
|
|||||||
focusMode={focusMode}
|
focusMode={focusMode}
|
||||||
onFocusLayerMouseEnter={onFocusLayerMouseEnter}
|
onFocusLayerMouseEnter={onFocusLayerMouseEnter}
|
||||||
/>
|
/>
|
||||||
<div className="h-full w-full flex gap-2">
|
)}
|
||||||
|
<div className={clsx("h-full w-full flex gap-2")}>
|
||||||
|
{!hideSidebar && (
|
||||||
<Sidebar
|
<Sidebar
|
||||||
path={router.pathname}
|
path={router.pathname}
|
||||||
navDisabled={navDisabled}
|
navDisabled={navDisabled}
|
||||||
@@ -37,10 +41,12 @@ export default function Layout({user, children, className, bgColor="bg-white", n
|
|||||||
className="-md:hidden"
|
className="-md:hidden"
|
||||||
user={user}
|
user={user}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
`w-full min-h-full md:mr-8 ${bgColor} shadow-md rounded-2xl p-4 xl:p-10 pb-8 flex flex-col gap-8 relative overflow-hidden mt-2`,
|
`w-full min-h-full ${bgColor} shadow-md rounded-2xl p-4 xl:p-10 pb-8 flex flex-col gap-8 relative overflow-hidden mt-2`,
|
||||||
bgColor !== "bg-white" ? "justify-center" : "h-fit",
|
bgColor !== "bg-white" ? "justify-center" : "h-fit",
|
||||||
|
hideSidebar ? "md:mx-8" : "md:mr-8",
|
||||||
className,
|
className,
|
||||||
)}>
|
)}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -25,13 +25,16 @@ import useSessions from "@/hooks/useSessions";
|
|||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import useGradingSystem from "@/hooks/useGrading";
|
import useGradingSystem from "@/hooks/useGrading";
|
||||||
|
import { Assignment } from "@/interfaces/results";
|
||||||
|
import { mapBy } from "@/utils";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
page: "exams" | "exercises";
|
page: "exams" | "exercises";
|
||||||
user: User;
|
user: User;
|
||||||
|
hideSidebar?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ExamPage({page, user}: Props) {
|
export default function ExamPage({page, user, hideSidebar = false}: Props) {
|
||||||
const [variant, setVariant] = useState<Variant>("full");
|
const [variant, setVariant] = useState<Variant>("full");
|
||||||
const [avoidRepeated, setAvoidRepeated] = useState(false);
|
const [avoidRepeated, setAvoidRepeated] = useState(false);
|
||||||
const [hasBeenUploaded, setHasBeenUploaded] = useState(false);
|
const [hasBeenUploaded, setHasBeenUploaded] = useState(false);
|
||||||
@@ -210,7 +213,7 @@ export default function ExamPage({page, user}: Props) {
|
|||||||
}, [setModuleIndex, showSolutions]);
|
}, [setModuleIndex, showSolutions]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
console.log(selectedModules)
|
||||||
if (selectedModules.length > 0 && exams.length > 0 && moduleIndex < selectedModules.length) {
|
if (selectedModules.length > 0 && exams.length > 0 && moduleIndex < selectedModules.length) {
|
||||||
const nextExam = exams[moduleIndex];
|
const nextExam = exams[moduleIndex];
|
||||||
|
|
||||||
@@ -218,7 +221,6 @@ export default function ExamPage({page, user}: Props) {
|
|||||||
if (exerciseIndex === -1 && !["reading", "listening"].includes(nextExam?.module)) setExerciseIndex(0);
|
if (exerciseIndex === -1 && !["reading", "listening"].includes(nextExam?.module)) setExerciseIndex(0);
|
||||||
setExam(nextExam ? updateExamWithUserSolutions(nextExam) : undefined);
|
setExam(nextExam ? updateExamWithUserSolutions(nextExam) : undefined);
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [selectedModules, moduleIndex, exams]);
|
}, [selectedModules, moduleIndex, exams]);
|
||||||
|
|
||||||
@@ -520,6 +522,7 @@ export default function ExamPage({page, user}: Props) {
|
|||||||
<Layout
|
<Layout
|
||||||
user={user}
|
user={user}
|
||||||
bgColor={bgColor}
|
bgColor={bgColor}
|
||||||
|
hideSidebar={hideSidebar}
|
||||||
className="justify-between"
|
className="justify-between"
|
||||||
focusMode={selectedModules.length !== 0 && !showSolutions && moduleIndex < selectedModules.length}
|
focusMode={selectedModules.length !== 0 && !showSolutions && moduleIndex < selectedModules.length}
|
||||||
onFocusLayerMouseEnter={() => setShowAbandonPopup(true)}>
|
onFocusLayerMouseEnter={() => setShowAbandonPopup(true)}>
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res, params})
|
|||||||
const entity = await getEntityWithRoles(assignment.entity || "")
|
const entity = await getEntityWithRoles(assignment.entity || "")
|
||||||
if (!entity) return redirect("/assignments")
|
if (!entity) return redirect("/assignments")
|
||||||
|
|
||||||
if (!doesEntityAllow(user, entity, 'view_assignments')) return redirect("/assignments")
|
if (!doesEntityAllow(user, entity, 'view_assignments') && !["admin", "developer"].includes(user.type))
|
||||||
|
return redirect("/assignments")
|
||||||
|
|
||||||
const users = await (checkAccess(user, ["developer", "admin"]) ? getUsers() : getEntityUsers(entity.id));
|
const users = await (checkAccess(user, ["developer", "admin"]) ? getUsers() : getEntityUsers(entity.id));
|
||||||
|
|
||||||
|
|||||||
@@ -119,15 +119,6 @@ export default function AssignmentsPage({assignments, corporateAssignments, enti
|
|||||||
<b>Total:</b> {activeAssignments.reduce((acc, curr) => acc + curr.results.length, 0)}/
|
<b>Total:</b> {activeAssignments.reduce((acc, curr) => acc + curr.results.length, 0)}/
|
||||||
{activeAssignments.reduce((acc, curr) => curr.exams.length + acc, 0)}
|
{activeAssignments.reduce((acc, curr) => curr.exams.length + acc, 0)}
|
||||||
</span>
|
</span>
|
||||||
{Object.keys(groupBy(corporateAssignments, (x) => x.corporate?.id)).map((x) => (
|
|
||||||
<div key={x}>
|
|
||||||
<span className="font-semibold">{getUserCompanyName(users.find((u) => u.id === x)!, users, groups)}: </span>
|
|
||||||
<span>
|
|
||||||
{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)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import {Entity, EntityWithRoles} from "@/interfaces/entity";
|
|||||||
import {User} from "@/interfaces/user";
|
import {User} from "@/interfaces/user";
|
||||||
import {sessionOptions} from "@/lib/session";
|
import {sessionOptions} from "@/lib/session";
|
||||||
import {USER_TYPE_LABELS} from "@/resources/user";
|
import {USER_TYPE_LABELS} from "@/resources/user";
|
||||||
import {mapBy, redirect, serialize} from "@/utils";
|
import {filterBy, mapBy, redirect, serialize} from "@/utils";
|
||||||
import {getEntities, getEntitiesWithRoles} from "@/utils/entities.be";
|
import {getEntities, getEntitiesWithRoles} from "@/utils/entities.be";
|
||||||
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
||||||
import {getUserName} from "@/utils/users";
|
import {getUserName} from "@/utils/users";
|
||||||
import {getLinkedUsers} from "@/utils/users.be";
|
import {getEntitiesUsers, getLinkedUsers} from "@/utils/users.be";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {withIronSessionSsr} from "iron-session/next";
|
import {withIronSessionSsr} from "iron-session/next";
|
||||||
@@ -22,7 +22,7 @@ import Head from "next/head";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import {useRouter} from "next/router";
|
import {useRouter} from "next/router";
|
||||||
import {Divider} from "primereact/divider";
|
import {Divider} from "primereact/divider";
|
||||||
import {useState} from "react";
|
import {useMemo, useState} from "react";
|
||||||
import {BsCheck, BsChevronLeft, BsClockFill, BsEnvelopeFill, BsStopwatchFill} from "react-icons/bs";
|
import {BsCheck, BsChevronLeft, BsClockFill, BsEnvelopeFill, BsStopwatchFill} from "react-icons/bs";
|
||||||
import {toast, ToastContainer} from "react-toastify";
|
import {toast, ToastContainer} from "react-toastify";
|
||||||
import { requestUser } from "@/utils/api";
|
import { requestUser } from "@/utils/api";
|
||||||
@@ -34,12 +34,12 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
|||||||
|
|
||||||
if (shouldRedirectHome(user)) return redirect("/")
|
if (shouldRedirectHome(user)) return redirect("/")
|
||||||
|
|
||||||
const linkedUsers = await getLinkedUsers(user.id, user.type);
|
const users = await getEntitiesUsers(mapBy(user.entities, 'id'))
|
||||||
const entities = await getEntitiesWithRoles(mapBy(user.entities, "id"));
|
const entities = await getEntitiesWithRoles(mapBy(user.entities, "id"));
|
||||||
const allowedEntities = findAllowedEntities(user, entities, "create_classroom")
|
const allowedEntities = findAllowedEntities(user, entities, "create_classroom")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: serialize({user, entities: allowedEntities, users: linkedUsers.users.filter((x) => x.id !== user.id)}),
|
props: serialize({user, entities: allowedEntities, users: users.filter((x) => x.id !== user.id)}),
|
||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
@@ -55,7 +55,9 @@ export default function Home({user, users, entities}: Props) {
|
|||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [entity, setEntity] = useState<string | undefined>(entities[0]?.id);
|
const [entity, setEntity] = useState<string | undefined>(entities[0]?.id);
|
||||||
|
|
||||||
const {rows, renderSearch} = useListSearch<User>([["name"], ["corporateInformation", "companyInformation", "name"]], users);
|
const entityUsers = useMemo(() => !entity ? users : users.filter(u => mapBy(u.entities, 'id').includes(entity)), [entity, users])
|
||||||
|
|
||||||
|
const {rows, renderSearch} = useListSearch<User>([["name"], ["corporateInformation", "companyInformation", "name"]], entityUsers);
|
||||||
const {items, renderMinimal} = usePagination<User>(rows, 16);
|
const {items, renderMinimal} = usePagination<User>(rows, 16);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|||||||
@@ -6,15 +6,52 @@ import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
|||||||
import ExamPage from "./(exam)/ExamPage";
|
import ExamPage from "./(exam)/ExamPage";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import {User} from "@/interfaces/user";
|
import {User} from "@/interfaces/user";
|
||||||
import { redirect, serialize } from "@/utils";
|
import { filterBy, findBy, redirect, serialize } from "@/utils";
|
||||||
import { requestUser } from "@/utils/api";
|
import { requestUser } from "@/utils/api";
|
||||||
|
import { getAssignment, getAssignments, getAssignmentsByAssignee } from "@/utils/assignments.be";
|
||||||
|
import { Assignment } from "@/interfaces/results";
|
||||||
|
import useExamStore from "@/stores/examStore";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { Exam } from "@/interfaces/exam";
|
||||||
|
import { getExamsByIds } from "@/utils/exams.be";
|
||||||
|
import { sortByModule } from "@/utils/moduleUtils";
|
||||||
|
import { uniqBy } from "lodash";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { getSessionByAssignment, getSessionsByUser } from "@/utils/sessions.be";
|
||||||
|
import { Session } from "@/hooks/useSessions";
|
||||||
|
import moment from "moment";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(async ({req, res, query}) => {
|
||||||
const user = await requestUser(req, res)
|
const user = await requestUser(req, res)
|
||||||
if (!user) return redirect("/login")
|
const destination = Buffer.from(req.url || "/").toString("base64")
|
||||||
|
if (!user) return redirect(`/login?destination=${destination}`)
|
||||||
|
|
||||||
if (shouldRedirectHome(user)) return redirect("/")
|
if (shouldRedirectHome(user)) return redirect("/")
|
||||||
|
|
||||||
|
const {assignment: assignmentID} = query as {assignment?: string}
|
||||||
|
|
||||||
|
if (assignmentID) {
|
||||||
|
const assignment = await getAssignment(assignmentID)
|
||||||
|
|
||||||
|
if (!assignment) return redirect("/exam")
|
||||||
|
if (!assignment.assignees.includes(user.id) && !["admin", "developer"].includes(user.type))
|
||||||
|
return redirect("/exam")
|
||||||
|
|
||||||
|
const exams = await getExamsByIds(uniqBy(assignment.exams, "id"))
|
||||||
|
const session = await getSessionByAssignment(assignmentID)
|
||||||
|
|
||||||
|
if (
|
||||||
|
filterBy(assignment.results, 'user', user.id).length > 0 ||
|
||||||
|
moment(assignment.startDate).isAfter(moment()) ||
|
||||||
|
moment(assignment.endDate).isBefore(moment())
|
||||||
|
)
|
||||||
|
return redirect("/exam")
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: serialize({user, assignment, exams, session})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: serialize({user}),
|
props: serialize({user}),
|
||||||
};
|
};
|
||||||
@@ -22,9 +59,55 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: User;
|
user: User;
|
||||||
|
assignment?: Assignment
|
||||||
|
exams?: Exam[]
|
||||||
|
session?: Session
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Page({user}: Props) {
|
export default function Page({user, assignment, exams = [], session}: Props) {
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const state = useExamStore((state) => state)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (assignment && exams.length > 0 && !state.assignment && !session) {
|
||||||
|
state.setUserSolutions([]);
|
||||||
|
state.setShowSolutions(false);
|
||||||
|
state.setAssignment(assignment);
|
||||||
|
state.setExams(exams.sort(sortByModule));
|
||||||
|
state.setSelectedModules(
|
||||||
|
exams
|
||||||
|
.map((x) => x!)
|
||||||
|
.sort(sortByModule)
|
||||||
|
.map((x) => x!.module),
|
||||||
|
);
|
||||||
|
|
||||||
|
router.replace(router.asPath)
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [assignment, exams, session])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (assignment && exams.length > 0 && !state.assignment && !!session) {
|
||||||
|
state.setShuffles(session.userSolutions.map((x) => ({exerciseID: x.exercise, shuffles: x.shuffleMaps ? x.shuffleMaps : []})));
|
||||||
|
state.setSelectedModules(session.selectedModules);
|
||||||
|
state.setExam(session.exam);
|
||||||
|
state.setExams(session.exams);
|
||||||
|
state.setSessionId(session.sessionId);
|
||||||
|
state.setAssignment(session.assignment);
|
||||||
|
state.setExerciseIndex(session.exerciseIndex);
|
||||||
|
state.setPartIndex(session.partIndex);
|
||||||
|
state.setModuleIndex(session.moduleIndex);
|
||||||
|
state.setTimeSpent(session.timeSpent);
|
||||||
|
state.setUserSolutions(session.userSolutions);
|
||||||
|
state.setShowSolutions(false);
|
||||||
|
state.setQuestionIndex(session.questionIndex);
|
||||||
|
|
||||||
|
router.replace(router.asPath)
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [assignment, exams, session])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@@ -36,7 +119,7 @@ export default function Page({user}: Props) {
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<ExamPage page="exams" user={user} />
|
<ExamPage page="exams" user={user} hideSidebar={!!assignment} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,51 @@ import {shouldRedirectHome} from "@/utils/navigation.disabled";
|
|||||||
import ExamPage from "./(exam)/ExamPage";
|
import ExamPage from "./(exam)/ExamPage";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import {User} from "@/interfaces/user";
|
import {User} from "@/interfaces/user";
|
||||||
import { redirect, serialize } from "@/utils";
|
import { filterBy, findBy, redirect, serialize } from "@/utils";
|
||||||
import { requestUser } from "@/utils/api";
|
import { requestUser } from "@/utils/api";
|
||||||
|
import { getAssignment, getAssignments, getAssignmentsByAssignee } from "@/utils/assignments.be";
|
||||||
|
import { Assignment } from "@/interfaces/results";
|
||||||
|
import useExamStore from "@/stores/examStore";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { Exam } from "@/interfaces/exam";
|
||||||
|
import { getExamsByIds } from "@/utils/exams.be";
|
||||||
|
import { sortByModule } from "@/utils/moduleUtils";
|
||||||
|
import { uniqBy } from "lodash";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { getSessionByAssignment, getSessionsByUser } from "@/utils/sessions.be";
|
||||||
|
import { Session } from "@/hooks/useSessions";
|
||||||
|
import moment from "moment";
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(async ({req, res, query}) => {
|
||||||
const user = await requestUser(req, res)
|
const user = await requestUser(req, res)
|
||||||
if (!user) return redirect("/login")
|
const destination = Buffer.from(req.url || "/").toString("base64")
|
||||||
|
if (!user) return redirect(`/login?destination=${destination}`)
|
||||||
|
|
||||||
if (shouldRedirectHome(user)) return redirect("/")
|
if (shouldRedirectHome(user)) return redirect("/")
|
||||||
|
|
||||||
|
const {assignment: assignmentID} = query as {assignment?: string}
|
||||||
|
|
||||||
|
if (assignmentID) {
|
||||||
|
const assignment = await getAssignment(assignmentID)
|
||||||
|
|
||||||
|
if (!assignment) return redirect("/exercises")
|
||||||
|
if (!["admin", "developer"].includes(user.type) && !assignment.assignees.includes(user.id)) return redirect("/exercises")
|
||||||
|
|
||||||
|
const exams = await getExamsByIds(uniqBy(assignment.exams, "id"))
|
||||||
|
const session = await getSessionByAssignment(assignmentID)
|
||||||
|
|
||||||
|
if (
|
||||||
|
filterBy(assignment.results, 'user', user.id) ||
|
||||||
|
moment(assignment.startDate).isBefore(moment()) ||
|
||||||
|
moment(assignment.endDate).isAfter(moment())
|
||||||
|
)
|
||||||
|
return redirect("/exercises")
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: serialize({user, assignment, exams, session})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: serialize({user}),
|
props: serialize({user}),
|
||||||
};
|
};
|
||||||
@@ -22,13 +58,59 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: User;
|
user: User;
|
||||||
|
assignment?: Assignment
|
||||||
|
exams?: Exam[]
|
||||||
|
session?: Session
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Page({user}: Props) {
|
export default function Page({user, assignment, exams = [], session}: Props) {
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const state = useExamStore((state) => state)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (assignment && exams.length > 0 && !state.assignment && !session) {
|
||||||
|
state.setUserSolutions([]);
|
||||||
|
state.setShowSolutions(false);
|
||||||
|
state.setAssignment(assignment);
|
||||||
|
state.setExams(exams.sort(sortByModule));
|
||||||
|
state.setSelectedModules(
|
||||||
|
exams
|
||||||
|
.map((x) => x!)
|
||||||
|
.sort(sortByModule)
|
||||||
|
.map((x) => x!.module),
|
||||||
|
);
|
||||||
|
|
||||||
|
router.replace(router.asPath)
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [assignment, exams, session])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (assignment && exams.length > 0 && !state.assignment && !!session) {
|
||||||
|
state.setShuffles(session.userSolutions.map((x) => ({exerciseID: x.exercise, shuffles: x.shuffleMaps ? x.shuffleMaps : []})));
|
||||||
|
state.setSelectedModules(session.selectedModules);
|
||||||
|
state.setExam(session.exam);
|
||||||
|
state.setExams(session.exams);
|
||||||
|
state.setSessionId(session.sessionId);
|
||||||
|
state.setAssignment(session.assignment);
|
||||||
|
state.setExerciseIndex(session.exerciseIndex);
|
||||||
|
state.setPartIndex(session.partIndex);
|
||||||
|
state.setModuleIndex(session.moduleIndex);
|
||||||
|
state.setTimeSpent(session.timeSpent);
|
||||||
|
state.setUserSolutions(session.userSolutions);
|
||||||
|
state.setShowSolutions(false);
|
||||||
|
state.setQuestionIndex(session.questionIndex);
|
||||||
|
|
||||||
|
router.replace(router.asPath)
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [assignment, exams, session])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Exercises | EnCoach</title>
|
<title>Exams | EnCoach</title>
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="A training platform for the IELTS exam provided by the Muscat Training Institute and developed by eCrop."
|
content="A training platform for the IELTS exam provided by the Muscat Training Institute and developed by eCrop."
|
||||||
@@ -36,7 +118,7 @@ export default function Page({user}: Props) {
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<ExamPage page="exercises" user={user} />
|
<ExamPage page="exams" user={user} hideSidebar={!!assignment} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,16 +20,17 @@ import { redirect } from "@/utils";
|
|||||||
|
|
||||||
const EMAIL_REGEX = new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/g);
|
const EMAIL_REGEX = new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$/g);
|
||||||
|
|
||||||
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
|
export const getServerSideProps = withIronSessionSsr(async ({req, res, query}) => {
|
||||||
|
const destination = !query.destination ? "/" : Buffer.from(query.destination as string, 'base64').toString()
|
||||||
const user = await requestUser(req, res)
|
const user = await requestUser(req, res)
|
||||||
if (user) return redirect("/")
|
if (user) return redirect(destination)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {user: null},
|
props: {user: null, destination},
|
||||||
};
|
};
|
||||||
}, sessionOptions);
|
}, sessionOptions);
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login({ destination }: { destination: string }) {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [rememberPassword, setRememberPassword] = useState(false);
|
const [rememberPassword, setRememberPassword] = useState(false);
|
||||||
@@ -38,13 +39,13 @@ export default function Login() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const {user, mutateUser} = useUser({
|
const {user, mutateUser} = useUser({
|
||||||
redirectTo: "/",
|
redirectTo: destination,
|
||||||
redirectIfFound: true,
|
redirectIfFound: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user) router.push("/");
|
if (user) router.push(destination);
|
||||||
}, [router, user]);
|
}, [router, user, destination]);
|
||||||
|
|
||||||
const forgotPassword = () => {
|
const forgotPassword = () => {
|
||||||
if (!email || email.length < 0 || !EMAIL_REGEX.test(email)) {
|
if (!email || email.length < 0 || !EMAIL_REGEX.test(email)) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {getUserCorporate} from "./groups.be";
|
|||||||
import {Db, ObjectId} from "mongodb";
|
import {Db, ObjectId} from "mongodb";
|
||||||
import client from "@/lib/mongodb";
|
import client from "@/lib/mongodb";
|
||||||
import {MODULE_ARRAY} from "./moduleUtils";
|
import {MODULE_ARRAY} from "./moduleUtils";
|
||||||
|
import { mapBy } from ".";
|
||||||
|
|
||||||
const db = client.db(process.env.MONGODB_DB);
|
const db = client.db(process.env.MONGODB_DB);
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ export const getExamsByIds = async (ids: {module: Module; id: string}[]) => {
|
|||||||
async (m) =>
|
async (m) =>
|
||||||
await db
|
await db
|
||||||
.collection(m)
|
.collection(m)
|
||||||
.find<Exam>({id: {$in: groupedByModule[m]}})
|
.find<Exam>({id: {$in: mapBy(groupedByModule[m], 'id')}})
|
||||||
.toArray(),
|
.toArray(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,3 +9,8 @@ export const getSessionsByUser = async (id: string, limit?: number) =>
|
|||||||
.find<Session>({user: id})
|
.find<Session>({user: id})
|
||||||
.limit(limit || 0)
|
.limit(limit || 0)
|
||||||
.toArray();
|
.toArray();
|
||||||
|
|
||||||
|
export const getSessionByAssignment = async (assignmentID: string) =>
|
||||||
|
await db
|
||||||
|
.collection("sessions")
|
||||||
|
.findOne<Session>({"assignment.id": assignmentID})
|
||||||
|
|||||||
Reference in New Issue
Block a user