ENCOA-316 ENCOA-317:
Refactor components to remove Layout wrapper and pass it in the App component , implemented a skeleton feedback while loading page and improved API calls related to Dashboard/User Profile
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { User } from "@/interfaces/user";
|
||||
import { IncomingMessage, ServerResponse } from "http";
|
||||
import { IronSession } from "iron-session";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { getUser } from "./users.be";
|
||||
|
||||
|
||||
@@ -27,13 +27,72 @@ export const getAssignment = async (id: string) => {
|
||||
return await db.collection("assignments").findOne<Assignment>({ id });
|
||||
};
|
||||
|
||||
export const getAssignmentsByAssignee = async (id: string, filter?: { [key in keyof Partial<Assignment>]: any }) => {
|
||||
return await db
|
||||
.collection("assignments")
|
||||
.find<Assignment>({ assignees: id, ...(!filter ? {} : filter) })
|
||||
export const getAssignmentsByAssignee = async (id: string, filter?: {}, projection?: {}, sort?: {}) => {
|
||||
return await db.collection("assignments")
|
||||
.aggregate([
|
||||
{ $match: { assignees: id, ...(!filter ? {} : filter) } },
|
||||
...(sort ? [{ $sort: sort }] : []),
|
||||
...(projection ? [{ $project: projection }] : []),
|
||||
])
|
||||
.toArray();
|
||||
};
|
||||
|
||||
export const getAssignmentsForStudent = async (id: string, currentDate: string) => {
|
||||
return await db.collection("assignments")
|
||||
.aggregate([{
|
||||
$match: {
|
||||
assignees: id, archived: { $ne: true },
|
||||
endDate: { $gte: currentDate },
|
||||
$or: [
|
||||
{ autoStart: true, startDate: { $lt: currentDate } },
|
||||
{ start: true },
|
||||
],
|
||||
}
|
||||
},
|
||||
{ $sort: { startDate: 1 } },
|
||||
{
|
||||
$project: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
startDate: 1,
|
||||
endDate: 1,
|
||||
exams: {
|
||||
$sortArray: {
|
||||
input: {
|
||||
$filter: {
|
||||
input: "$exams",
|
||||
as: "exam",
|
||||
cond: { $eq: ["$$exam.assignee", id] },
|
||||
},
|
||||
},
|
||||
sortBy: { module: 1 },
|
||||
},
|
||||
},
|
||||
hasResults: {
|
||||
$cond: {
|
||||
if: {
|
||||
$gt: [
|
||||
{
|
||||
$size: {
|
||||
$filter: {
|
||||
input: "$results",
|
||||
as: "result",
|
||||
cond: { $eq: ["$$result.userId", id] },
|
||||
},
|
||||
},
|
||||
},
|
||||
0,
|
||||
],
|
||||
},
|
||||
then: true,
|
||||
else: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
]).toArray()
|
||||
};
|
||||
|
||||
export const getAssignmentsByAssignerBetweenDates = async (id: string, startDate: Date, endDate: Date) => {
|
||||
return await db.collection("assignments").find<Assignment>({ assigner: id }).toArray();
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ export const getEntitiesWithRoles = async (ids?: string[]): Promise<EntityWithRo
|
||||
export const getEntities = async (ids?: string[], projection = {}) => {
|
||||
return await db
|
||||
.collection("entities")
|
||||
.find<Entity>(ids ? { id: { $in: ids } } : {}, projection)
|
||||
.find<Entity>(ids ? { id: { $in: ids } } : {}, { projection })
|
||||
.toArray();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { CEFR_STEPS } from "@/resources/grading";
|
||||
import { getUserCorporate } from "@/utils/groups.be";
|
||||
import { User } from "@/interfaces/user";
|
||||
import { Grading } from "@/interfaces";
|
||||
import client from "@/lib/mongodb";
|
||||
|
||||
const db = client.db(process.env.MONGODB_DB);
|
||||
|
||||
export const getGradingSystemByEntity = async (id: string) =>
|
||||
(await db.collection("grading").findOne<Grading>({ entity: id })) || { steps: CEFR_STEPS, entity: "" };
|
||||
export const getGradingSystemByEntity = async (id: string, projection?: {}) =>
|
||||
(await db.collection("grading").findOne<Grading>({ entity: id }, {
|
||||
projection: projection,
|
||||
})) || { steps: CEFR_STEPS, entity: "" };
|
||||
|
||||
export const getGradingSystemByEntities = async (ids: string[]) =>
|
||||
await db.collection("grading").find<Grading>({ entity: { $in: ids } }).toArray();
|
||||
|
||||
@@ -82,8 +82,8 @@ export const getGroups = async (): Promise<WithEntity<Group>[]> => {
|
||||
.aggregate<WithEntity<Group>>(addEntityToGroupPipeline).toArray()
|
||||
};
|
||||
|
||||
export const getParticipantGroups = async (id: string) => {
|
||||
return await db.collection("groups").find<Group>({ participants: id }).toArray();
|
||||
export const getParticipantGroups = async (id: string, projection?: {}) => {
|
||||
return await db.collection("groups").find<Group>({ participants: id }, { projection: projection }).toArray();
|
||||
};
|
||||
|
||||
export const getParticipantsGroups = async (ids: string[]) => {
|
||||
|
||||
@@ -36,6 +36,43 @@ export function checkAccess(user: User, types: Type[], permissions?: PermissionT
|
||||
return true;
|
||||
}
|
||||
|
||||
export function groupAllowedEntitiesByPermissions(
|
||||
user: User,
|
||||
entities: EntityWithRoles[],
|
||||
permissions: RolePermission[]
|
||||
): { [key: string]: EntityWithRoles[] } {
|
||||
if (["admin", "developer"].includes(user?.type)) {
|
||||
return permissions.reduce((acc, permission) => {
|
||||
acc[permission] = entities;
|
||||
return acc;
|
||||
}, {} as { [key: string]: EntityWithRoles[] });
|
||||
}
|
||||
|
||||
const userEntityMap = new Map(user.entities.map(e => [e.id, e]));
|
||||
const roleCache = new Map<string, Role | null>();
|
||||
|
||||
return entities.reduce((acc, entity) => {
|
||||
const userEntity = userEntityMap.get(entity.id);
|
||||
const role = userEntity
|
||||
? roleCache.get(userEntity.role) ??
|
||||
(() => {
|
||||
const foundRole = entity.roles.find(r => r.id === userEntity.role) || null;
|
||||
roleCache.set(userEntity.role, foundRole);
|
||||
return foundRole;
|
||||
})()
|
||||
: null;
|
||||
|
||||
permissions.forEach(permission => {
|
||||
if (!acc[permission]) acc[permission] = [];
|
||||
if (role && role.permissions.includes(permission)) {
|
||||
acc[permission].push(entity);
|
||||
}
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, {} as { [key: string]: EntityWithRoles[] });
|
||||
}
|
||||
|
||||
export function findAllowedEntities(user: User, entities: EntityWithRoles[], permission: RolePermission) {
|
||||
if (["admin", "developer"].includes(user?.type)) return entities
|
||||
|
||||
@@ -52,7 +89,6 @@ export function findAllowedEntitiesSomePermissions(user: User, entities: EntityW
|
||||
|
||||
export function doesEntityAllow(user: User, entity: EntityWithRoles, permission: RolePermission) {
|
||||
if (isAdmin(user)) return true
|
||||
|
||||
const userEntity = findBy(user.entities, 'id', entity?.id)
|
||||
if (!userEntity) return false
|
||||
|
||||
|
||||
@@ -1,12 +1,140 @@
|
||||
import {Stat} from "@/interfaces/user";
|
||||
import { Stat } from "@/interfaces/user";
|
||||
import client from "@/lib/mongodb";
|
||||
|
||||
const db = client.db(process.env.MONGODB_DB);
|
||||
|
||||
export const getStatsByUser = async (id: string) => await db.collection("stats").find<Stat>({user: id}).toArray();
|
||||
export const getStatsByUser = async (id: string) => await db.collection("stats").find<Stat>({ user: id }).toArray();
|
||||
|
||||
export const getStatsByUsers = async (ids: string[]) =>
|
||||
await db
|
||||
.collection("stats")
|
||||
.find<Stat>({user: {$in: ids}})
|
||||
.find<Stat>({ user: { $in: ids } })
|
||||
.toArray();
|
||||
|
||||
export const getDetailedStatsByUser = async (id: string, query?: string) => {
|
||||
let aggregateArray: any[] = [
|
||||
{ $match: { user: id } },
|
||||
{ $sort: { "date": 1 } },
|
||||
]
|
||||
switch (query) {
|
||||
case "stats":
|
||||
{
|
||||
aggregateArray = aggregateArray.concat([{
|
||||
$group: {
|
||||
_id: "$session",
|
||||
modules: { $addToSet: "$module" },
|
||||
documents: { $push: "$$ROOT" },
|
||||
totalCorrect: { $sum: "$score.correct" },
|
||||
totalQuestions: { $sum: "$score.total" }
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
hasAllModules: {
|
||||
$eq: [
|
||||
{ $size: { $setIntersection: ["$modules", ["reading", "listening", "writing", "speaking"]] } },
|
||||
4
|
||||
]
|
||||
},
|
||||
uniqueModulesCount: { $size: "$modules" },
|
||||
averageScore: {
|
||||
$cond: [
|
||||
{ $gt: ["$totalQuestions", 0] },
|
||||
{ $multiply: [{ $divide: ["$totalCorrect", "$totalQuestions"] }, 100] },
|
||||
0
|
||||
]
|
||||
},
|
||||
documents: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
fullExams: { $sum: { $cond: ["$hasAllModules", 1, 0] } },
|
||||
uniqueModules: { $sum: "$uniqueModulesCount" },
|
||||
averageScore: {
|
||||
$avg: "$averageScore"
|
||||
},
|
||||
allStats: { $push: "$documents" }
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
fullExams: 1,
|
||||
uniqueModules: 1,
|
||||
averageScore: 1,
|
||||
allStats: {
|
||||
$reduce: {
|
||||
input: "$allStats",
|
||||
initialValue: [],
|
||||
in: { $concatArrays: ["$$value", "$$this"] }
|
||||
}
|
||||
}
|
||||
}
|
||||
}])
|
||||
}
|
||||
break;
|
||||
case "byModule": {
|
||||
aggregateArray = aggregateArray.concat([{
|
||||
$facet: {
|
||||
moduleCounts: [
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
module: "$module",
|
||||
session: "$session"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: "$_id.module",
|
||||
count: {
|
||||
$count: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
module: "$_id",
|
||||
count: "$count"
|
||||
}
|
||||
}
|
||||
],
|
||||
allDocuments: [
|
||||
{
|
||||
$project: {
|
||||
_id: 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
moduleCount: {
|
||||
$arrayToObject: {
|
||||
$map: {
|
||||
input: "$moduleCounts",
|
||||
as: "module",
|
||||
in: {
|
||||
k: "$$module.module",
|
||||
v: "$$module.count"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
allDocs: "$allDocuments"
|
||||
}
|
||||
}])
|
||||
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return await db.collection("stats").aggregate(aggregateArray).toArray().then((result) => query ? result[0] : result
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import client from "@/lib/mongodb";
|
||||
import { EntityWithRoles, WithEntities } from "@/interfaces/entity";
|
||||
import { getEntity } from "./entities.be";
|
||||
import { getRole } from "./roles.be";
|
||||
import { findAllowedEntities } from "./permissions";
|
||||
import { findAllowedEntities, groupAllowedEntitiesByPermissions } from "./permissions";
|
||||
import { mapBy } from ".";
|
||||
|
||||
const db = client.db(process.env.MONGODB_DB);
|
||||
@@ -38,7 +38,7 @@ export async function searchUsers(searchInput?: string, limit = 50, page = 0, so
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const [{ users, totalUsers }] = await db
|
||||
.collection("users").aggregate([
|
||||
{
|
||||
@@ -75,6 +75,39 @@ export async function countUsers(filter?: object) {
|
||||
.countDocuments(filter || {})
|
||||
}
|
||||
|
||||
export async function countUsersByTypes(types: Type[]) {
|
||||
return await db
|
||||
.collection("users")
|
||||
.aggregate([
|
||||
{
|
||||
$match: {
|
||||
type: { $in: types } // Filter only specified types
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: "$type",
|
||||
count: { $sum: 1 } // Count documents in each group
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
counts: {
|
||||
$push: { k: "$_id", v: "$count" } // Convert to key-value pairs
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
result: { $arrayToObject: "$counts" } // Convert key-value pairs to an object
|
||||
}
|
||||
}
|
||||
]).toArray().then(([{ result }]) => result);
|
||||
|
||||
}
|
||||
|
||||
export async function getUserWithEntity(id: string): Promise<WithEntities<User> | undefined> {
|
||||
const user = await db.collection("users").findOne<User>({ id: id }, { projection: { _id: 0 } });
|
||||
if (!user) return undefined;
|
||||
@@ -91,8 +124,8 @@ export async function getUserWithEntity(id: string): Promise<WithEntities<User>
|
||||
return { ...user, entities };
|
||||
}
|
||||
|
||||
export async function getUser(id: string): Promise<User | undefined> {
|
||||
const user = await db.collection("users").findOne<User>({ id: id }, { projection: { _id: 0 } });
|
||||
export async function getUser(id: string, projection = {}): Promise<User | undefined> {
|
||||
const user = await db.collection("users").findOne<User>({ id: id }, { projection: { _id: 0, ...projection } });
|
||||
return !!user ? user : undefined;
|
||||
}
|
||||
|
||||
@@ -120,7 +153,7 @@ export async function countEntityUsers(id: string, filter?: object) {
|
||||
export async function getEntitiesUsers(ids: string[], filter?: object, limit?: number, projection = {}) {
|
||||
return await db
|
||||
.collection("users")
|
||||
.find<User>({ "entities.id": { $in: ids }, ...(filter || {}) }, projection)
|
||||
.find<User>({ "entities.id": { $in: ids }, ...(filter || {}) }, { projection })
|
||||
.limit(limit || 0)
|
||||
.toArray();
|
||||
}
|
||||
@@ -199,29 +232,45 @@ export async function getUserBalance(user: User) {
|
||||
}
|
||||
|
||||
export const filterAllowedUsers = async (user: User, entities: EntityWithRoles[]) => {
|
||||
const studentsAllowedEntities = findAllowedEntities(user, entities, 'view_students')
|
||||
const teachersAllowedEntities = findAllowedEntities(user, entities, 'view_teachers')
|
||||
const corporateAllowedEntities = findAllowedEntities(user, entities, 'view_corporates')
|
||||
const masterCorporateAllowedEntities = findAllowedEntities(user, entities, 'view_mastercorporates')
|
||||
|
||||
const {
|
||||
["view_students"]: allowedStudentEntities,
|
||||
["view_teachers"]: allowedTeacherEntities,
|
||||
["view_corporates"]: allowedCorporateEntities,
|
||||
["view_mastercorporates"]: allowedMasterCorporateEntities,
|
||||
} = groupAllowedEntitiesByPermissions(user, entities, [
|
||||
"view_students",
|
||||
"view_teachers",
|
||||
'view_corporates',
|
||||
'view_mastercorporates',
|
||||
]);
|
||||
|
||||
const students = await getEntitiesUsers(mapBy(studentsAllowedEntities, 'id'), { type: "student" })
|
||||
const teachers = await getEntitiesUsers(mapBy(teachersAllowedEntities, 'id'), { type: "teacher" })
|
||||
const corporates = await getEntitiesUsers(mapBy(corporateAllowedEntities, 'id'), { type: "corporate" })
|
||||
const masterCorporates = await getEntitiesUsers(mapBy(masterCorporateAllowedEntities, 'id'), { type: "mastercorporate" })
|
||||
|
||||
const students = await getEntitiesUsers(mapBy(allowedStudentEntities, 'id'), { type: "student" })
|
||||
const teachers = await getEntitiesUsers(mapBy(allowedTeacherEntities, 'id'), { type: "teacher" })
|
||||
const corporates = await getEntitiesUsers(mapBy(allowedCorporateEntities, 'id'), { type: "corporate" })
|
||||
const masterCorporates = await getEntitiesUsers(mapBy(allowedMasterCorporateEntities, 'id'), { type: "mastercorporate" })
|
||||
|
||||
return [...students, ...teachers, ...corporates, ...masterCorporates]
|
||||
}
|
||||
|
||||
export const countAllowedUsers = async (user: User, entities: EntityWithRoles[]) => {
|
||||
const studentsAllowedEntities = findAllowedEntities(user, entities, 'view_students')
|
||||
const teachersAllowedEntities = findAllowedEntities(user, entities, 'view_teachers')
|
||||
const corporateAllowedEntities = findAllowedEntities(user, entities, 'view_corporates')
|
||||
const masterCorporateAllowedEntities = findAllowedEntities(user, entities, 'view_mastercorporates')
|
||||
const {
|
||||
["view_students"]: allowedStudentEntities,
|
||||
["view_teachers"]: allowedTeacherEntities,
|
||||
["view_corporates"]: allowedCorporateEntities,
|
||||
["view_mastercorporates"]: allowedMasterCorporateEntities,
|
||||
} = groupAllowedEntitiesByPermissions(user, entities, [
|
||||
"view_students",
|
||||
"view_teachers",
|
||||
'view_corporates',
|
||||
'view_mastercorporates',
|
||||
]);
|
||||
|
||||
const student = await countEntitiesUsers(mapBy(studentsAllowedEntities, 'id'), { type: "student" })
|
||||
const teacher = await countEntitiesUsers(mapBy(teachersAllowedEntities, 'id'), { type: "teacher" })
|
||||
const corporate = await countEntitiesUsers(mapBy(corporateAllowedEntities, 'id'), { type: "corporate" })
|
||||
const mastercorporate = await countEntitiesUsers(mapBy(masterCorporateAllowedEntities, 'id'), { type: "mastercorporate" })
|
||||
const student = await countEntitiesUsers(mapBy(allowedStudentEntities, 'id'), { type: "student" })
|
||||
const teacher = await countEntitiesUsers(mapBy(allowedTeacherEntities, 'id'), { type: "teacher" })
|
||||
const corporate = await countEntitiesUsers(mapBy(allowedCorporateEntities, 'id'), { type: "corporate" })
|
||||
const mastercorporate = await countEntitiesUsers(mapBy(allowedMasterCorporateEntities, 'id'), { type: "mastercorporate" })
|
||||
|
||||
return { student, teacher, corporate, mastercorporate }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user