Refactor most getServerProps to fetch independent request in parallel and projected the data only to return the necessary fields and changed some functions

This commit is contained in:
José Marques Lima
2025-01-30 18:25:42 +00:00
parent 58aebaa66c
commit 98ba0bfc04
36 changed files with 5796 additions and 4058 deletions

View File

@@ -4,20 +4,15 @@ import IconCard from "@/components/IconCard";
import { EntityWithRoles } from "@/interfaces/entity";
import { Stat, Type, User } from "@/interfaces/user";
import { sessionOptions } from "@/lib/session";
import { filterBy, mapBy, redirect, serialize } from "@/utils";
import { filterBy, mapBy, redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
import {
countEntitiesAssignments,
} from "@/utils/assignments.be";
import { countEntitiesAssignments } from "@/utils/assignments.be";
import { getEntities } from "@/utils/entities.be";
import { countGroups } from "@/utils/groups.be";
import { checkAccess } from "@/utils/permissions";
import { groupByExam } from "@/utils/stats";
import { getStatsByUsers } from "@/utils/stats.be";
import {
countUsersByTypes,
getUsers,
} from "@/utils/users.be";
import { countUsersByTypes, getUsers } from "@/utils/users.be";
import { withIronSessionSsr } from "iron-session/next";
import Head from "next/head";
import { useRouter } from "next/router";
@@ -49,49 +44,48 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
if (!user || !user.isVerified) return redirect("/login");
if (!checkAccess(user, ["admin", "developer"])) return redirect("/");
const students = await getUsers(
{ type: "student" },
10,
{
averageLevel: -1,
},
{ id: 1, name: 1, email: 1, profilePicture: 1 }
);
const usersCount = await countUsersByTypes([
"student",
"teacher",
"corporate",
"mastercorporate",
const [
entities,
usersCount,
groupsCount,
students,
latestStudents,
latestTeachers,
] = await Promise.all([
getEntities(undefined, { _id: 0, id: 1, label: 1 }),
countUsersByTypes(["student", "teacher", "corporate", "mastercorporate"]),
countGroups(),
getUsers(
{ type: "student" },
10,
{
averageLevel: -1,
},
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
getUsers(
{ type: "student" },
10,
{
registrationDate: -1,
},
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
getUsers(
{ type: "teacher" },
10,
{
registrationDate: -1,
},
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
]);
const latestStudents = await getUsers(
{ type: "student" },
10,
{
registrationDate: -1,
},
{ id: 1, name: 1, email: 1, profilePicture: 1 }
);
const latestTeachers = await getUsers(
{ type: "teacher" },
10,
{
registrationDate: -1,
},
{ id: 1, name: 1, email: 1, profilePicture: 1 }
);
const entities = await getEntities(undefined, { _id: 0, id: 1, label: 1 });
const assignmentsCount = await countEntitiesAssignments(
mapBy(entities, "id"),
{ archived: { $ne: true } }
);
const groupsCount = await countGroups();
const stats = await getStatsByUsers(mapBy(students, "id"));
return {

View File

@@ -14,10 +14,7 @@ import {
groupAllowedEntitiesByPermissions,
} from "@/utils/permissions";
import { groupByExam } from "@/utils/stats";
import {
countAllowedUsers,
getUsers,
} from "@/utils/users.be";
import { countAllowedUsers, getUsers } from "@/utils/users.be";
import { withIronSessionSsr } from "iron-session/next";
import moment from "moment";
import Head from "next/head";
@@ -35,6 +32,7 @@ import {
import { ToastContainer } from "react-toastify";
import { useAllowedEntities } from "@/hooks/useEntityPermissions";
import { isAdmin } from "@/utils/users";
import { count } from "console";
interface Props {
user: User;
@@ -68,40 +66,44 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
"view_teachers",
]);
const allowedStudentEntitiesIDS = mapBy(allowedStudentEntities, "id");
const entitiesIDS = mapBy(entities, "id") || [];
const allowedStudentEntitiesIDS = mapBy(allowedStudentEntities, "id");
const entitiesIDS = mapBy(entities, "id") || [];
const students = await getUsers(
{ type: "student", "entities.id": { $in: allowedStudentEntitiesIDS } },
10,
{ averageLevel: -1 },
{ id: 1, name: 1, email: 1, profilePicture: 1 }
);
const latestStudents = await getUsers(
{ type: "student", "entities.id": { $in: allowedStudentEntitiesIDS } },
10,
{ registrationDate: -1 },
{ id: 1, name: 1, email: 1, profilePicture: 1 }
);
const latestTeachers = await getUsers(
{
type: "teacher",
"entities.id": { $in: mapBy(allowedTeacherEntities, "id") },
},
10,
{ registrationDate: -1 },
{ id: 1, name: 1, email: 1, profilePicture: 1 }
);
const userCounts = await countAllowedUsers(user, entities);
const assignmentsCount = await countEntitiesAssignments(
entitiesIDS,
{ archived: { $ne: true } }
);
const groupsCount = await countGroupsByEntities(entitiesIDS);
const [
students,
latestStudents,
latestTeachers,
userCounts,
assignmentsCount,
groupsCount,
] = await Promise.all([
getUsers(
{ type: "student", "entities.id": { $in: allowedStudentEntitiesIDS } },
10,
{ averageLevel: -1 },
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
getUsers(
{ type: "student", "entities.id": { $in: allowedStudentEntitiesIDS } },
10,
{ registrationDate: -1 },
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
getUsers(
{
type: "teacher",
"entities.id": { $in: mapBy(allowedTeacherEntities, "id") },
},
10,
{ registrationDate: -1 },
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
countAllowedUsers(user, entities),
countEntitiesAssignments(entitiesIDS, {
archived: { $ne: true },
}),
countGroupsByEntities(entitiesIDS),
]);
return {
props: serialize({
@@ -135,8 +137,8 @@ export default function Dashboard({
userCounts.student +
userCounts.teacher,
[userCounts]
);
);
const totalLicenses = useMemo(
() =>
entities.reduce(

View File

@@ -2,21 +2,16 @@
import UserDisplayList from "@/components/UserDisplayList";
import IconCard from "@/components/IconCard";
import { EntityWithRoles } from "@/interfaces/entity";
import { Stat, Type, User } from "@/interfaces/user";
import { Stat, Type, User } from "@/interfaces/user";
import { sessionOptions } from "@/lib/session";
import { filterBy, mapBy, redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
import {
countEntitiesAssignments,
} from "@/utils/assignments.be";
import { countEntitiesAssignments } from "@/utils/assignments.be";
import { getEntities } from "@/utils/entities.be";
import { countGroups } from "@/utils/groups.be";
import { checkAccess } from "@/utils/permissions";
import { groupByExam } from "@/utils/stats";
import {
countUsersByTypes,
getUsers,
} from "@/utils/users.be";
import { countUsersByTypes, getUsers } from "@/utils/users.be";
import { withIronSessionSsr } from "iron-session/next";
import Head from "next/head";
import { useRouter } from "next/router";
@@ -49,45 +44,41 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
if (!checkAccess(user, ["admin", "developer"])) return redirect("/");
const students = await getUsers(
{ type: "student" },
10,
{
averageLevel: -1,
},
{ id: 1, name: 1, email: 1, profilePicture: 1 }
);
const usersCount = await countUsersByTypes([
"student",
"teacher",
"corporate",
"mastercorporate",
const [
students,
latestStudents,
latestTeachers,
usersCount,
entities,
groupsCount,
] = await Promise.all([
getUsers(
{ type: "student" },
10,
{ averageLevel: -1 },
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
getUsers(
{ type: "student" },
10,
{ registrationDate: -1 },
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
getUsers(
{ type: "teacher" },
10,
{ registrationDate: -1 },
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
countUsersByTypes(["student", "teacher", "corporate", "mastercorporate"]),
getEntities(undefined, { _id: 0, id: 1, label: 1 }),
countGroups(),
]);
const latestStudents = await getUsers(
{ type: "student" },
10,
{
registrationDate: -1,
},
{id:1, name: 1, email: 1, profilePicture: 1 }
);
const latestTeachers = await getUsers(
{ type: "teacher" },
10,
{
registrationDate: -1,
},
{ id:1,name: 1, email: 1, profilePicture: 1 }
);
const entities = await getEntities(undefined, { _id: 0, id: 1, label: 1 });
const assignmentsCount = await countEntitiesAssignments(
mapBy(entities, "id"),
{ archived: { $ne: true } }
);
const groupsCount = await countGroups();
return {
props: serialize({

View File

@@ -34,6 +34,7 @@ import {
} from "react-icons/bs";
import { ToastContainer } from "react-toastify";
import { isAdmin } from "@/utils/users";
import { count } from "console";
interface Props {
user: User;
@@ -70,37 +71,39 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const entitiesIDS = mapBy(entities, "id") || [];
const students = await getUsers(
{ type: "student", "entities.id": { $in: allowedStudentEntitiesIDS } },
10,
{ averageLevel: -1 },
{ id: 1, name: 1, email: 1, profilePicture: 1 }
);
const latestStudents = await getUsers(
{ type: "student", "entities.id": { $in: allowedStudentEntitiesIDS } },
10,
{ registrationDate: -1 },
{ id: 1, name: 1, email: 1, profilePicture: 1 }
);
const latestTeachers = await getUsers(
{
type: "teacher",
"entities.id": { $in: mapBy(allowedTeacherEntities, "id") },
},
10,
{ registrationDate: -1 },
{ id: 1, name: 1, email: 1, profilePicture: 1 }
);
const userCounts = await countAllowedUsers(user, entities);
const assignmentsCount = await countEntitiesAssignments(entitiesIDS, {
archived: { $ne: true },
});
const groupsCount = await countGroupsByEntities(entitiesIDS);
const [
students,
latestStudents,
latestTeachers,
userCounts,
assignmentsCount,
groupsCount,
] = await Promise.all([
getUsers(
{ type: "student", "entities.id": { $in: allowedStudentEntitiesIDS } },
10,
{ averageLevel: -1 },
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
getUsers(
{ type: "student", "entities.id": { $in: allowedStudentEntitiesIDS } },
10,
{ registrationDate: -1 },
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
getUsers(
{
type: "teacher",
"entities.id": { $in: mapBy(allowedTeacherEntities, "id") },
},
10,
{ registrationDate: -1 },
{ _id: 0, id: 1, name: 1, email: 1, profilePicture: 1 }
),
countAllowedUsers(user, entities),
countEntitiesAssignments(entitiesIDS, { archived: { $ne: true } }),
countGroupsByEntities(entitiesIDS),
]);
return {
props: serialize({
@@ -127,6 +130,7 @@ export default function Dashboard({
stats = [],
groupsCount,
}: Props) {
const totalCount = useMemo(
() =>
userCounts.corporate +

View File

@@ -5,7 +5,7 @@ import InviteWithUserCard from "@/components/Medium/InviteWithUserCard";
import ModuleBadge from "@/components/ModuleBadge";
import ProfileSummary from "@/components/ProfileSummary";
import { Session } from "@/hooks/useSessions";
import { Grading } from "@/interfaces";
import { Grading, Module } from "@/interfaces";
import { EntityWithRoles } from "@/interfaces/entity";
import { Exam } from "@/interfaces/exam";
import { InviteWithEntity } from "@/interfaces/invite";
@@ -34,6 +34,7 @@ import { capitalize, uniqBy } from "lodash";
import moment from "moment";
import Head from "next/head";
import { useRouter } from "next/router";
import { useMemo } from "react";
import {
BsBook,
BsClipboard,
@@ -65,42 +66,49 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
return redirect("/");
const entityIDS = mapBy(user.entities, "id") || [];
const entities = await getEntities(entityIDS, { _id: 0, label: 1 });
const currentDate = moment().toISOString();
const assignments = await getAssignmentsForStudent(user.id, currentDate);
const stats = await getDetailedStatsByUser(user.id, "stats");
const [assignments, stats, invites, grading] = await Promise.all([
getAssignmentsForStudent(user.id, currentDate),
getDetailedStatsByUser(user.id, "stats"),
getInvitesByInvitee(user.id),
getGradingSystemByEntity(entityIDS[0] || "", {
_id: 0,
steps: 1,
}),
]);
const assignmentsIDs = mapBy(assignments, "id");
const sessions = await getSessionsByUser(user.id, 10, {
["assignment.id"]: { $in: assignmentsIDs },
});
const invites = await getInvitesByInvitee(user.id);
const grading = await getGradingSystemByEntity(entityIDS[0] || "", {
_id: 0,
steps: 1,
});
const formattedInvites = await Promise.all(
invites.map(convertInvitersToEntity)
);
const examIDs = uniqBy(
assignments.flatMap((a) =>
a.exams.map((e: { module: string; id: string }) => ({
module: e.module,
id: e.id,
key: `${e.module}_${e.id}`,
}))
assignments.reduce<{ module: Module; id: string; key: string }[]>(
(acc, a) => {
a.exams.forEach((e: { module: Module; id: string }) => {
acc.push({
module: e.module,
id: e.id,
key: `${e.module}_${e.id}`,
});
});
return acc;
},
[]
),
"key"
);
const exams = examIDs.length > 0 ? await getExamsByIds(examIDs) : [];
return {
props: serialize({
user,
entities,
assignments,
stats,
exams,
@@ -145,6 +153,11 @@ export default function Dashboard({
}
};
const entitiesLabels = useMemo(
() => (entities.length > 0 ? mapBy(entities, "label")?.join(", ") : ""),
[entities]
);
return (
<>
<Head>
@@ -160,7 +173,7 @@ export default function Dashboard({
<>
{entities.length > 0 && (
<div className="rounded-lg bg-neutral-200 px-2 py-1 ">
<b>{mapBy(entities, "label")?.join(", ")}</b>
<b>{entitiesLabels}</b>
</div>
)}

View File

@@ -27,6 +27,7 @@ import { requestUser } from "@/utils/api";
import { useAllowedEntities } from "@/hooks/useEntityPermissions";
import { getEntitiesUsers } from "@/utils/users.be";
import { isAdmin } from "@/utils/users";
import { useMemo } from "react";
interface Props {
user: User;
@@ -52,29 +53,29 @@ export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => {
const filteredEntities = findAllowedEntities(user, entities, "view_students");
const students = await getEntitiesUsers(
mapBy(filteredEntities, "id"),
{
type: "student",
},
0,
{
_id: 0,
id: 1,
name: 1,
email: 1,
profilePicture: 1,
levels: 1,
registrationDate: 1,
}
);
const assignments = await getEntitiesAssignments(entityIDS);
const [students, assignments, groups] = await Promise.all([
getEntitiesUsers(
mapBy(filteredEntities, "id"),
{
type: "student",
},
0,
{
_id: 0,
id: 1,
name: 1,
email: 1,
profilePicture: 1,
levels: 1,
registrationDate: 1,
}
),
getEntitiesAssignments(entityIDS),
getGroupsByEntities(entityIDS),
]);
const stats = await getStatsByUsers(students.map((u) => u.id));
const groups = await getGroupsByEntities(entityIDS);
return {
props: serialize({ user, students, entities, assignments, stats, groups }),
};
@@ -100,6 +101,10 @@ export default function Dashboard({
entities,
"view_student_performance"
);
const entitiesLabels = useMemo(
() => mapBy(entities, "label")?.join(", "),
[entities]
);
return (
<>
@@ -117,7 +122,7 @@ export default function Dashboard({
<div className="w-full flex flex-col gap-4">
{entities.length > 0 && (
<div className="w-fit self-end bg-neutral-200 px-2 rounded-lg py-1">
<b>{mapBy(entities, "label")?.join(", ")}</b>
<b>{entitiesLabels}</b>
</div>
)}
<section className="grid grid-cols-5 -md:grid-cols-2 place-items-center gap-4 text-center">