Merged main into bugfix/mongo-migration-bugs
This commit is contained in:
@@ -28,6 +28,7 @@ import {checkAccess} from "@/utils/permissions";
|
||||
import {PermissionType} from "@/interfaces/permissions";
|
||||
import usePermissions from "@/hooks/usePermissions";
|
||||
import useUserBalance from "@/hooks/useUserBalance";
|
||||
import usePagination from "@/hooks/usePagination";
|
||||
const columnHelper = createColumnHelper<User>();
|
||||
const searchFields = [["name"], ["email"], ["corporateInformation", "companyInformation", "name"]];
|
||||
|
||||
@@ -62,15 +63,12 @@ export default function UserList({
|
||||
const [sorter, setSorter] = useState<string>();
|
||||
const [displayUsers, setDisplayUsers] = useState<User[]>([]);
|
||||
const [selectedUser, setSelectedUser] = useState<User>();
|
||||
const [page, setPage] = useState(0);
|
||||
|
||||
const userHash = useMemo(
|
||||
() => ({
|
||||
type,
|
||||
size: 16,
|
||||
page,
|
||||
}),
|
||||
[type, page],
|
||||
[type],
|
||||
);
|
||||
|
||||
const {users, total, isLoading, reload} = useUsers(userHash);
|
||||
@@ -102,9 +100,9 @@ export default function UserList({
|
||||
(async () => {
|
||||
if (users && users.length > 0) {
|
||||
const filteredUsers = filters.reduce((d, f) => d.filter(f), users);
|
||||
const sortedUsers = await asyncSorter<User>(filteredUsers, sortFunction);
|
||||
// const sortedUsers = await asyncSorter<User>(filteredUsers, sortFunction);
|
||||
|
||||
setDisplayUsers([...sortedUsers]);
|
||||
setDisplayUsers([...filteredUsers]);
|
||||
}
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -513,10 +511,14 @@ export default function UserList({
|
||||
return a.id.localeCompare(b.id);
|
||||
};
|
||||
|
||||
const {rows: filteredRows, renderSearch} = useListSearch<User>(searchFields, displayUsers);
|
||||
const {rows: filteredRows, renderSearch, text: searchText} = useListSearch<User>(searchFields, displayUsers);
|
||||
const {items, setPage, render: renderPagination} = usePagination<User>(filteredRows, 16);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(() => setPage(0), [searchText]);
|
||||
|
||||
const table = useReactTable({
|
||||
data: filteredRows,
|
||||
data: items,
|
||||
columns: (!showDemographicInformation ? defaultColumns : demographicColumns) as any,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
});
|
||||
@@ -632,27 +634,7 @@ export default function UserList({
|
||||
Download List
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full flex gap-2 justify-between">
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
className="w-full max-w-[200px]"
|
||||
disabled={page === 0}
|
||||
onClick={() => setPage((prev) => prev - 1)}>
|
||||
Previous Page
|
||||
</Button>
|
||||
<div className="flex items-center gap-4 w-fit">
|
||||
<span className="opacity-80">
|
||||
{page * userHash.size + 1} - {(page + 1) * userHash.size > total ? total : (page + 1) * userHash.size} / {total}
|
||||
</span>
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
className="w-[200px]"
|
||||
disabled={(page + 1) * userHash.size >= total}
|
||||
onClick={() => setPage((prev) => prev + 1)}>
|
||||
Next Page
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{renderPagination()}
|
||||
<table className="rounded-xl bg-mti-purple-ultralight/40 w-full">
|
||||
<thead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
|
||||
@@ -22,7 +22,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
async function GET(req: NextApiRequest, res: NextApiResponse) {
|
||||
const {id} = req.query as {id: string};
|
||||
|
||||
const assigners = await getAllAssignersByCorporate(id);
|
||||
const assigners = await getAllAssignersByCorporate(id, req.session.user!.type);
|
||||
const assignments = await getAssignmentsByAssigners([...assigners, id]);
|
||||
|
||||
res.status(200).json(uniqBy(assignments, "id"));
|
||||
|
||||
@@ -29,7 +29,7 @@ async function GET(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const idsList = ids.split(",");
|
||||
|
||||
const assignments = await getAssignmentsForCorporates(idsList, startDateParsed, endDateParsed);
|
||||
const assignments = await getAssignmentsForCorporates(req.session.user!.type, idsList, startDateParsed, endDateParsed);
|
||||
res.status(200).json(assignments);
|
||||
} catch (err: any) {
|
||||
res.status(500).json({error: err.message});
|
||||
|
||||
@@ -14,12 +14,18 @@ import {getGradingSystem} from "@/utils/grading.be";
|
||||
import {StudentUser, User} from "@/interfaces/user";
|
||||
import {calculateBandScore, getGradingLabel} from "@/utils/score";
|
||||
import {Module} from "@/interfaces";
|
||||
import {uniq} from "lodash";
|
||||
import {getUserName} from "@/utils/users";
|
||||
import {LevelExam} from "@/interfaces/exam";
|
||||
import {getSpecificExams} from "@/utils/exams.be";
|
||||
|
||||
export default withIronSessionApiRoute(handler, sessionOptions);
|
||||
|
||||
interface TableData {
|
||||
user: string;
|
||||
studentID: string;
|
||||
passportID: string;
|
||||
exams: string;
|
||||
email: string;
|
||||
correct: number;
|
||||
corporate: string;
|
||||
@@ -41,7 +47,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method === "POST") return await post(req, res);
|
||||
}
|
||||
|
||||
const searchFilters = [["email"], ["user"], ["userId"]];
|
||||
const searchFilters = [["email"], ["user"], ["userId"], ["assignment"], ["exams"]];
|
||||
|
||||
async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
// verify if it's a logged user that is trying to export
|
||||
@@ -64,12 +70,13 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
};
|
||||
const startDateParsed = startDate ? new Date(startDate) : undefined;
|
||||
const endDateParsed = endDate ? new Date(endDate) : undefined;
|
||||
const assignments = await getAssignmentsForCorporates(ids, startDateParsed, endDateParsed);
|
||||
const assignments = await getAssignmentsForCorporates(req.session.user.type, ids, startDateParsed, endDateParsed);
|
||||
|
||||
const assignmentUsers = [...new Set(assignments.flatMap((a) => a.assignees))];
|
||||
const assignmentUsers = uniq([...assignments.flatMap((x) => x.assignees), ...assignments.flatMap((x) => x.assigner)]);
|
||||
const assigners = [...new Set(assignments.map((a) => a.assigner))];
|
||||
const users = await getSpecificUsers(assignmentUsers);
|
||||
const assignerUsers = await getSpecificUsers(assigners);
|
||||
const exams = await getSpecificExams(uniq(assignments.flatMap((x) => x.exams.map((x) => x.id))));
|
||||
|
||||
const assignerUsersGradingSystems = await Promise.all(
|
||||
assignerUsers.map(async (user: User) => {
|
||||
@@ -91,7 +98,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (gradingSystem) {
|
||||
const bandScore = calculateBandScore(correct, total, "level", user?.focus || "academic");
|
||||
return {
|
||||
label: getGradingLabel(bandScore, gradingSystem?.steps || []),
|
||||
label: getGradingLabel(bandScore, gradingSystem.steps || []),
|
||||
score: bandScore,
|
||||
};
|
||||
}
|
||||
@@ -113,10 +120,12 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
const commonData = {
|
||||
user: userData?.name || "",
|
||||
email: userData?.email || "",
|
||||
studentID: (userData as StudentUser).studentID || "",
|
||||
studentID: (userData as StudentUser)?.studentID || "",
|
||||
passportID: (userData as StudentUser)?.demographicInformation?.passport_id || "",
|
||||
userId: assignee,
|
||||
exams: a.exams.map((x) => x.id).join(", "),
|
||||
corporateId: a.corporateId,
|
||||
corporate: corporateUser?.name || "",
|
||||
corporate: !corporateUser ? "" : getUserName(corporateUser),
|
||||
assignment: a.name,
|
||||
level,
|
||||
score,
|
||||
@@ -130,14 +139,22 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
};
|
||||
}
|
||||
|
||||
const partsData = userStats.every((e) => e.module === "level")
|
||||
? userStats.reduce((acc, e, index) => {
|
||||
return {
|
||||
...acc,
|
||||
[`part${index}`]: `${e.score.correct}/${e.score.total}`,
|
||||
};
|
||||
}, {})
|
||||
: {};
|
||||
let data: {total: number; correct: number}[] = [];
|
||||
if (a.exams.every((x) => x.module === "level")) {
|
||||
const exam = exams.find((x) => x.id === a.exams.find((x) => x.assignee === assignee)?.id) as LevelExam;
|
||||
data = exam.parts.map((x) => {
|
||||
const exerciseIDs = x.exercises.map((x) => x.id);
|
||||
const stats = userStats.filter((x) => exerciseIDs.includes(x.exercise));
|
||||
|
||||
const total = stats.reduce((acc, curr) => acc + curr.score.total, 0);
|
||||
const correct = stats.reduce((acc, curr) => acc + curr.score.correct, 0);
|
||||
|
||||
return {total, correct};
|
||||
});
|
||||
}
|
||||
|
||||
const partsData =
|
||||
data.length > 0 ? data.reduce((acc, e, index) => ({...acc, [`part${index}`]: `${e.correct}/${e.total}`}), {}) : {};
|
||||
|
||||
return {
|
||||
...commonData,
|
||||
@@ -169,6 +186,10 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
label: "Student ID",
|
||||
value: (entry: TableData) => entry.studentID,
|
||||
},
|
||||
{
|
||||
label: "Passport ID",
|
||||
value: (entry: TableData) => entry.passportID,
|
||||
},
|
||||
...(displaySelection
|
||||
? [
|
||||
{
|
||||
@@ -186,7 +207,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
value: (entry: TableData) => (entry.submitted ? "Yes" : "No"),
|
||||
},
|
||||
{
|
||||
label: "Correct",
|
||||
label: "Score",
|
||||
value: (entry: TableData) => entry.correct,
|
||||
},
|
||||
{
|
||||
@@ -206,7 +227,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
})),
|
||||
];
|
||||
|
||||
const filteredSearch = searchText ? search(searchText, searchFilters, tableResults) : tableResults;
|
||||
const filteredSearch = !!searchText ? search(searchText, searchFilters, tableResults) : tableResults;
|
||||
|
||||
worksheet.addRow(headers.map((h) => h.label));
|
||||
(filteredSearch as TableData[]).forEach((entry) => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import {withIronSessionApiRoute} from "iron-session/next";
|
||||
import {sessionOptions} from "@/lib/session";
|
||||
import {getLinkedUsers} from "@/utils/users.be";
|
||||
import {Type} from "@/interfaces/user";
|
||||
import {uniqBy} from "lodash";
|
||||
|
||||
export default withIronSessionApiRoute(handler, sessionOptions);
|
||||
|
||||
@@ -31,5 +32,5 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
direction,
|
||||
);
|
||||
|
||||
res.status(200).json({users, total});
|
||||
res.status(200).json({users: uniqBy([...users], "id"), total});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user