ENCOA-279

This commit is contained in:
Tiago Ribeiro
2024-12-12 15:06:00 +00:00
parent 3e74827c47
commit 240e36f15a
11 changed files with 71 additions and 73 deletions

View File

@@ -1,15 +1,15 @@
/* eslint-disable @next/next/no-img-element */ /* eslint-disable @next/next/no-img-element */
import {toast, ToastContainer} from "react-toastify"; import { toast, ToastContainer } from "react-toastify";
import axios from "axios"; import axios from "axios";
import {FormEvent, useEffect, useState} from "react"; import { FormEvent, useEffect, useState } from "react";
import Head from "next/head"; import Head from "next/head";
import useUser from "@/hooks/useUser"; import useUser from "@/hooks/useUser";
import {Divider} from "primereact/divider"; import { Divider } from "primereact/divider";
import Button from "@/components/Low/Button"; import Button from "@/components/Low/Button";
import {BsArrowRepeat} from "react-icons/bs"; import { BsArrowRepeat } from "react-icons/bs";
import Link from "next/link"; import Link from "next/link";
import Input from "@/components/Low/Input"; import Input from "@/components/Low/Input";
import {useRouter} from "next/router"; import { useRouter } from "next/router";
export function getServerSideProps({ export function getServerSideProps({
query, query,
@@ -35,12 +35,12 @@ export function getServerSideProps({
props: { props: {
code: query.oobCode, code: query.oobCode,
mode: query.mode, mode: query.mode,
...(query.continueUrl ? {continueUrl: query.continueUrl} : {}), ...(query.continueUrl ? { continueUrl: query.continueUrl } : {}),
}, },
}; };
} }
export default function Reset({code, mode, continueUrl}: {code: string; mode: string; continueUrl?: string}) { export default function Reset({ code, mode, continueUrl }: { code: string; mode: string; continueUrl?: string }) {
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -54,7 +54,7 @@ export default function Reset({code, mode, continueUrl}: {code: string; mode: st
useEffect(() => { useEffect(() => {
if (mode === "signIn") { if (mode === "signIn") {
axios axios
.post<{ok: boolean}>("/api/reset/verify", { .post<{ ok: boolean }>("/api/reset/verify", {
email: continueUrl?.replace("https://platform.encoach.com/", "").replace("https://staging.encoach.com/", ""), email: continueUrl?.replace("https://platform.encoach.com/", "").replace("https://staging.encoach.com/", ""),
}) })
.then((response) => { .then((response) => {
@@ -64,7 +64,7 @@ export default function Reset({code, mode, continueUrl}: {code: string; mode: st
}); });
setTimeout(() => { setTimeout(() => {
router.push("/"); router.push("/");
}, 1000); }, 500);
return; return;
} }
@@ -86,7 +86,7 @@ export default function Reset({code, mode, continueUrl}: {code: string; mode: st
setIsLoading(true); setIsLoading(true);
axios axios
.post<{ok: boolean}>("/api/reset/confirm", {code, password}) .post<{ ok: boolean }>("/api/reset/confirm", { code, password })
.then((response) => { .then((response) => {
if (response.data.ok) { if (response.data.ok) {
toast.success("Your password has been reset!", { toast.success("Your password has been reset!", {
@@ -98,10 +98,10 @@ export default function Reset({code, mode, continueUrl}: {code: string; mode: st
return; return;
} }
toast.error("Something went wrong! Please make sure to click the link in your e-mail again!", {toastId: "reset-error"}); toast.error("Something went wrong! Please make sure to click the link in your e-mail again!", { toastId: "reset-error" });
}) })
.catch(() => { .catch(() => {
toast.error("Something went wrong! Please make sure to click the link in your e-mail again!", {toastId: "reset-error"}); toast.error("Something went wrong! Please make sure to click the link in your e-mail again!", { toastId: "reset-error" });
}) })
.finally(() => setIsLoading(false)); .finally(() => setIsLoading(false));
}; };

View File

@@ -73,9 +73,8 @@ async function registerIndividual(req: NextApiRequest, res: NextApiResponse) {
...(passport_id ? { demographicInformation: { passport_id } } : {}), ...(passport_id ? { demographicInformation: { passport_id } } : {}),
registrationDate: new Date().toISOString(), registrationDate: new Date().toISOString(),
status: code ? "active" : "paymentDue", status: code ? "active" : "paymentDue",
// apparently there's an issue with the verification email system entities: [],
// therefore we will skip this requirement for now isVerified: !!codeDoc,
isVerified: true,
}; };
await db.collection("users").insertOne(user); await db.collection("users").insertOne(user);

View File

@@ -47,7 +47,7 @@ interface Props {
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res) const user = await requestUser(req, res)
if (!user) return redirect("/login") if (!user || !user.isVerified) return redirect("/login")
if (!checkAccess(user, ["admin", "developer"])) return redirect("/") if (!checkAccess(user, ["admin", "developer"])) return redirect("/")

View File

@@ -46,7 +46,7 @@ interface Props {
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res) const user = await requestUser(req, res)
if (!user) return redirect("/login") if (!user || !user.isVerified) return redirect("/login")
if (!checkAccess(user, ["admin", "developer", "corporate"])) return redirect("/") if (!checkAccess(user, ["admin", "developer", "corporate"])) return redirect("/")

View File

@@ -47,7 +47,7 @@ interface Props {
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res) const user = await requestUser(req, res)
if (!user) return redirect("/login") if (!user || !user.isVerified) return redirect("/login")
if (!checkAccess(user, ["admin", "developer"])) return redirect("/") if (!checkAccess(user, ["admin", "developer"])) return redirect("/")

View File

@@ -1,12 +1,12 @@
import {User} from "@/interfaces/user"; import { User } from "@/interfaces/user";
import {sessionOptions} from "@/lib/session"; import { sessionOptions } from "@/lib/session";
import { redirect } from "@/utils"; import { redirect } from "@/utils";
import { requestUser } from "@/utils/api"; import { requestUser } from "@/utils/api";
import {withIronSessionSsr} from "iron-session/next"; import { withIronSessionSsr } from "iron-session/next";
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res) const user = await requestUser(req, res)
if (!user) return redirect("/login") if (!user || !user.isVerified) return redirect("/login")
return redirect(`/dashboard/${user.type}`) return redirect(`/dashboard/${user.type}`)
}, sessionOptions); }, sessionOptions);

View File

@@ -52,7 +52,7 @@ interface Props {
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res) const user = await requestUser(req, res)
if (!user) return redirect("/login") if (!user || !user.isVerified) return redirect("/login")
if (!checkAccess(user, ["admin", "developer", "mastercorporate"])) if (!checkAccess(user, ["admin", "developer", "mastercorporate"]))
return redirect("/") return redirect("/")

View File

@@ -5,38 +5,38 @@ import ProgressBar from "@/components/Low/ProgressBar";
import InviteWithUserCard from "@/components/Medium/InviteWithUserCard"; import InviteWithUserCard from "@/components/Medium/InviteWithUserCard";
import ModuleBadge from "@/components/ModuleBadge"; import ModuleBadge from "@/components/ModuleBadge";
import ProfileSummary from "@/components/ProfileSummary"; import ProfileSummary from "@/components/ProfileSummary";
import {Session} from "@/hooks/useSessions"; import { Session } from "@/hooks/useSessions";
import {Grading} from "@/interfaces"; import { Grading } from "@/interfaces";
import {EntityWithRoles} from "@/interfaces/entity"; import { EntityWithRoles } from "@/interfaces/entity";
import {Exam} from "@/interfaces/exam"; import { Exam } from "@/interfaces/exam";
import { InviteWithEntity } from "@/interfaces/invite"; import { InviteWithEntity } from "@/interfaces/invite";
import {Assignment} from "@/interfaces/results"; import { Assignment } from "@/interfaces/results";
import {Stat, User} from "@/interfaces/user"; import { Stat, User } from "@/interfaces/user";
import {sessionOptions} from "@/lib/session"; import { sessionOptions } from "@/lib/session";
import useExamStore from "@/stores/exam"; import useExamStore from "@/stores/exam";
import {findBy, mapBy, redirect, serialize} from "@/utils"; import { findBy, mapBy, redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api"; import { requestUser } from "@/utils/api";
import {activeAssignmentFilter} from "@/utils/assignments"; import { activeAssignmentFilter } from "@/utils/assignments";
import {getAssignmentsByAssignee} from "@/utils/assignments.be"; import { getAssignmentsByAssignee } from "@/utils/assignments.be";
import {getEntitiesWithRoles, getEntityWithRoles} from "@/utils/entities.be"; import { getEntitiesWithRoles, getEntityWithRoles } from "@/utils/entities.be";
import {getExamsByIds} from "@/utils/exams.be"; import { getExamsByIds } from "@/utils/exams.be";
import {getGradingSystemByEntity} from "@/utils/grading.be"; import { getGradingSystemByEntity } from "@/utils/grading.be";
import {convertInvitersToEntity, getInvitesByInvitee} from "@/utils/invites.be"; import { convertInvitersToEntity, getInvitesByInvitee } from "@/utils/invites.be";
import {countExamModules, countFullExams, MODULE_ARRAY, sortByModule, sortByModuleName} from "@/utils/moduleUtils"; import { countExamModules, countFullExams, MODULE_ARRAY, sortByModule, sortByModuleName } from "@/utils/moduleUtils";
import {checkAccess} from "@/utils/permissions"; import { checkAccess } from "@/utils/permissions";
import {getGradingLabel} from "@/utils/score"; import { getGradingLabel } from "@/utils/score";
import {getSessionsByUser} from "@/utils/sessions.be"; import { getSessionsByUser } from "@/utils/sessions.be";
import {averageScore} from "@/utils/stats"; import { averageScore } from "@/utils/stats";
import {getStatsByUser} from "@/utils/stats.be"; import { getStatsByUser } from "@/utils/stats.be";
import clsx from "clsx"; import clsx from "clsx";
import {withIronSessionSsr} from "iron-session/next"; import { withIronSessionSsr } from "iron-session/next";
import {capitalize, uniqBy} from "lodash"; import { capitalize, uniqBy } from "lodash";
import moment from "moment"; import moment from "moment";
import Head from "next/head"; import Head from "next/head";
import {useRouter} from "next/router"; import { useRouter } from "next/router";
import { useMemo } from "react"; import { useMemo } from "react";
import {BsBook, BsClipboard, BsFileEarmarkText, BsHeadphones, BsMegaphone, BsPen, BsPencil, BsStar} from "react-icons/bs"; import { BsBook, BsClipboard, BsFileEarmarkText, BsHeadphones, BsMegaphone, BsPen, BsPencil, BsStar } from "react-icons/bs";
import {ToastContainer} from "react-toastify"; import { ToastContainer } from "react-toastify";
interface Props { interface Props {
user: User; user: User;
@@ -49,9 +49,9 @@ interface Props {
grading: Grading; grading: Grading;
} }
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res) const user = await requestUser(req, res)
if (!user) return redirect("/login") if (!user || !user.isVerified) return redirect("/login")
if (!checkAccess(user, ["admin", "developer", "student"])) if (!checkAccess(user, ["admin", "developer", "student"]))
return redirect("/") return redirect("/")
@@ -59,7 +59,7 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
const entityIDS = mapBy(user.entities, "id") || []; const entityIDS = mapBy(user.entities, "id") || [];
const entities = await getEntitiesWithRoles(entityIDS); const entities = await getEntitiesWithRoles(entityIDS);
const assignments = await getAssignmentsByAssignee(user.id, {archived: {$ne: true}}); const assignments = await getAssignmentsByAssignee(user.id, { archived: { $ne: true } });
const stats = await getStatsByUser(user.id); const stats = await getStatsByUser(user.id);
const sessions = await getSessionsByUser(user.id, 10); const sessions = await getSessionsByUser(user.id, 10);
const invites = await getInvitesByInvitee(user.id); const invites = await getInvitesByInvitee(user.id);
@@ -69,16 +69,16 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => {
const examIDs = uniqBy( const examIDs = uniqBy(
assignments.flatMap((a) => assignments.flatMap((a) =>
a.exams.filter((e) => e.assignee === user.id).map((e) => ({module: e.module, id: e.id, key: `${e.module}_${e.id}`})), a.exams.filter((e) => e.assignee === user.id).map((e) => ({ module: e.module, id: e.id, key: `${e.module}_${e.id}` })),
), ),
"key", "key",
); );
const exams = await getExamsByIds(examIDs); const exams = await getExamsByIds(examIDs);
return {props: serialize({user, entities, assignments, stats, exams, sessions, invites: formattedInvites, grading})}; return { props: serialize({ user, entities, assignments, stats, exams, sessions, invites: formattedInvites, grading }) };
}, sessionOptions); }, sessionOptions);
export default function Dashboard({user, entities, assignments, stats, invites, grading, sessions, exams}: Props) { export default function Dashboard({ user, entities, assignments, stats, invites, grading, sessions, exams }: Props) {
const router = useRouter(); const router = useRouter();
const dispatch = useExamStore((state) => state.dispatch); const dispatch = useExamStore((state) => state.dispatch);
@@ -90,11 +90,13 @@ export default function Dashboard({user, entities, assignments, stats, invites,
}) })
if (assignmentExams.every((x) => !!x)) { if (assignmentExams.every((x) => !!x)) {
dispatch({type: "INIT_EXAM", payload: { dispatch({
type: "INIT_EXAM", payload: {
exams: assignmentExams.sort(sortByModule), exams: assignmentExams.sort(sortByModule),
modules: mapBy(assignmentExams.sort(sortByModule), 'module'), modules: mapBy(assignmentExams.sort(sortByModule), 'module'),
assignment assignment
}}) }
})
router.push("/exam"); router.push("/exam");
} }

View File

@@ -39,7 +39,7 @@ interface Props {
export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res) const user = await requestUser(req, res)
if (!user) return redirect("/login") if (!user || !user.isVerified) return redirect("/login")
if (!checkAccess(user, ["admin", "developer", "teacher"])) if (!checkAccess(user, ["admin", "developer", "teacher"]))
return redirect("/") return redirect("/")

View File

@@ -1,12 +1,12 @@
import {User} from "@/interfaces/user"; import { User } from "@/interfaces/user";
import {sessionOptions} from "@/lib/session"; import { sessionOptions } from "@/lib/session";
import { redirect } from "@/utils"; import { redirect } from "@/utils";
import { requestUser } from "@/utils/api"; import { requestUser } from "@/utils/api";
import {withIronSessionSsr} from "iron-session/next"; import { withIronSessionSsr } from "iron-session/next";
export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const user = await requestUser(req, res) const user = await requestUser(req, res)
if (!user) return redirect("/login") if (!user || !user.isVerified) return redirect("/login")
return redirect(`/dashboard/${user.type}`) return redirect(`/dashboard/${user.type}`)
}, sessionOptions); }, sessionOptions);

View File

@@ -23,7 +23,7 @@ const EMAIL_REGEX = new RegExp(/^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:
export const getServerSideProps = withIronSessionSsr(async ({ req, res, query }) => { export const getServerSideProps = withIronSessionSsr(async ({ req, res, query }) => {
const destination = !query.destination ? "/" : Buffer.from(query.destination as string, 'base64').toString() 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(destination) if (user && user.isVerified) return redirect(destination)
return { return {
props: { user: null, destination }, props: { user: null, destination },
@@ -39,13 +39,10 @@ export default function Login({ destination = "/" }: { destination?: string }) {
const router = useRouter(); const router = useRouter();
const isOfficialExamLogin = useMemo(() => destination.startsWith("/official-exam"), [destination]) const isOfficialExamLogin = useMemo(() => destination.startsWith("/official-exam"), [destination])
const { user, mutateUser } = useUser({ const { user, mutateUser } = useUser();
redirectTo: destination,
redirectIfFound: true,
});
useEffect(() => { useEffect(() => {
if (user) router.push(destination); if (user && user.isVerified) router.push(destination);
}, [router, user, destination]); }, [router, user, destination]);
const forgotPassword = () => { const forgotPassword = () => {
@@ -173,7 +170,7 @@ export default function Login({ destination = "/" }: { destination?: string }) {
</span> </span>
</> </>
)} )}
{/* {user && !user.isVerified && <EmailVerification user={user} isLoading={isLoading} setIsLoading={setIsLoading} />} */} {user && !user.isVerified && <EmailVerification user={user} isLoading={isLoading} setIsLoading={setIsLoading} />}
</section> </section>
</main> </main>
</> </>