ENCOA-200: Added Master Statistical to Corporate

This commit is contained in:
Joao Ramos
2024-09-07 18:19:25 +01:00
parent 8c94bcac52
commit fb9d11f38d
4 changed files with 131 additions and 76 deletions

View File

@@ -26,6 +26,7 @@ import {
BsArrowRepeat, BsArrowRepeat,
BsPlus, BsPlus,
BsEnvelopePaper, BsEnvelopePaper,
BsDatabase,
} from "react-icons/bs"; } from "react-icons/bs";
import UserCard from "@/components/UserCard"; import UserCard from "@/components/UserCard";
import useGroups from "@/hooks/useGroups"; import useGroups from "@/hooks/useGroups";
@@ -52,7 +53,7 @@ import {getUserCompanyName} from "@/resources/user";
import {futureAssignmentFilter, pastAssignmentFilter, archivedAssignmentFilter, activeAssignmentFilter} from "@/utils/assignments"; import {futureAssignmentFilter, pastAssignmentFilter, archivedAssignmentFilter, activeAssignmentFilter} from "@/utils/assignments";
import useUserBalance from "@/hooks/useUserBalance"; import useUserBalance from "@/hooks/useUserBalance";
import AssignmentsPage from "./views/AssignmentsPage"; import AssignmentsPage from "./views/AssignmentsPage";
import MasterStatistical from "./MasterStatistical";
interface Props { interface Props {
user: CorporateUser; user: CorporateUser;
linkedCorporate?: CorporateUser | MasterCorporateUser; linkedCorporate?: CorporateUser | MasterCorporateUser;
@@ -278,6 +279,30 @@ export default function CorporateDashboard({user, linkedCorporate}: Props) {
return calculateAverageLevel(levels); return calculateAverageLevel(levels);
}; };
// this workaround will allow us toreuse the master statistical due to master corporate restraints
// while still being able to use the corporate user
const groupedByNameCorporateIds = useMemo(() => ({
[user.corporateInformation?.companyInformation?.name || user.name]: [user.id],
}), [user]);
const teachersAndStudents = useMemo(() => [...students, ...teachers], [students, teachers]);
const MasterStatisticalPage = () => {
return (
<>
<div className="flex flex-col gap-4">
<div
onClick={() => router.push("/")}
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
<BsArrowLeft className="text-xl" />
<span>Back</span>
</div>
<h2 className="text-2xl font-semibold">Master Statistical</h2>
</div>
<MasterStatistical users={teachersAndStudents} corporateUsers={groupedByNameCorporateIds} displaySelection={false} />
</>
);
};
const DefaultDashboard = () => ( const DefaultDashboard = () => (
<> <>
{!!linkedCorporate && ( {!!linkedCorporate && (
@@ -336,6 +361,12 @@ export default function CorporateDashboard({user, linkedCorporate}: Props) {
color="purple" color="purple"
onClick={() => router.push("/#studentsPerformance")} onClick={() => router.push("/#studentsPerformance")}
/> />
<IconCard
Icon={BsDatabase}
label="Master Statistical"
color="purple"
onClick={() => router.push("/#statistical")}
/>
<button <button
disabled={isAssignmentsLoading} disabled={isAssignmentsLoading}
onClick={() => router.push("/#assignments")} onClick={() => router.push("/#assignments")}
@@ -504,6 +535,7 @@ export default function CorporateDashboard({user, linkedCorporate}: Props) {
/> />
)} )}
{router.asPath === "/#studentsPerformance" && <StudentPerformancePage />} {router.asPath === "/#studentsPerformance" && <StudentPerformancePage />}
{router.asPath === "/#statistical" && <MasterStatisticalPage />}
{router.asPath === "/" && <DefaultDashboard />} {router.asPath === "/" && <DefaultDashboard />}
</> </>
); );

View File

@@ -500,7 +500,6 @@ export default function MasterCorporateDashboard({user}: Props) {
<IconCard <IconCard
Icon={BsDatabase} Icon={BsDatabase}
label="Master Statistical" label="Master Statistical"
// value={masterCorporateUserGroups.length}
color="purple" color="purple"
onClick={() => router.push("/#statistical")} onClick={() => router.push("/#statistical")}
/> />

View File

@@ -28,6 +28,7 @@ interface GroupedCorporateUsers {
interface Props { interface Props {
corporateUsers: GroupedCorporateUsers; corporateUsers: GroupedCorporateUsers;
users: User[]; users: User[];
displaySelection?: boolean;
} }
interface TableData { interface TableData {
@@ -49,7 +50,7 @@ interface UserCount {
const searchFilters = [["email"], ["user"], ["userId"]]; const searchFilters = [["email"], ["user"], ["userId"]];
const MasterStatistical = (props: Props) => { const MasterStatistical = (props: Props) => {
const { users, corporateUsers } = props; const { users, corporateUsers, displaySelection = true } = props;
// const corporateRelevantUsers = React.useMemo( // const corporateRelevantUsers = React.useMemo(
// () => corporateUsers.filter((x) => x.type !== "student") as CorporateUser[], // () => corporateUsers.filter((x) => x.type !== "student") as CorporateUser[],
@@ -173,6 +174,8 @@ const MasterStatistical = (props: Props) => {
return <span>{info.getValue()}</span>; return <span>{info.getValue()}</span>;
}, },
}), }),
...(displaySelection
? [
columnHelper.accessor("corporate", { columnHelper.accessor("corporate", {
header: "Corporate", header: "Corporate",
id: "corporate", id: "corporate",
@@ -180,6 +183,8 @@ const MasterStatistical = (props: Props) => {
return <span>{info.getValue()}</span>; return <span>{info.getValue()}</span>;
}, },
}), }),
]
: []),
columnHelper.accessor("assignment", { columnHelper.accessor("assignment", {
header: "Assignment", header: "Assignment",
id: "assignment", id: "assignment",
@@ -261,6 +266,7 @@ const MasterStatistical = (props: Props) => {
...(startDate ? { startDate: startDate.toISOString() } : {}), ...(startDate ? { startDate: startDate.toISOString() } : {}),
...(endDate ? { endDate: endDate.toISOString() } : {}), ...(endDate ? { endDate: endDate.toISOString() } : {}),
searchText, searchText,
displaySelection,
}); });
toast.success("Report ready!"); toast.success("Report ready!");
const link = document.createElement("a"); const link = document.createElement("a");
@@ -282,6 +288,7 @@ const MasterStatistical = (props: Props) => {
const consolidateResults = getStudentsConsolidateScore(); const consolidateResults = getStudentsConsolidateScore();
return ( return (
<> <>
{displaySelection && (
<div className="flex flex-wrap gap-2 items-center text-center"> <div className="flex flex-wrap gap-2 items-center text-center">
<IconCard <IconCard
Icon={BsBank} Icon={BsBank}
@@ -304,7 +311,9 @@ const MasterStatistical = (props: Props) => {
); );
const valueHash = getCorporatesScoresHash(group); const valueHash = getCorporatesScoresHash(group);
const value = getConsolidateScoreStr(getConsolidateScore(valueHash)); const value = getConsolidateScoreStr(
getConsolidateScore(valueHash)
);
return ( return (
<IconCard <IconCard
key={corporateName} key={corporateName}
@@ -328,6 +337,7 @@ const MasterStatistical = (props: Props) => {
); );
})} })}
</div> </div>
)}
<div className="flex gap-3 w-full"> <div className="flex gap-3 w-full">
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<label className="font-normal text-base text-mti-gray-dim"> <label className="font-normal text-base text-mti-gray-dim">

View File

@@ -49,15 +49,22 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
// verify if it's a logged user that is trying to export // verify if it's a logged user that is trying to export
if (req.session.user) { if (req.session.user) {
if ( if (
!checkAccess(req.session.user, ["mastercorporate", "developer", "admin"]) !checkAccess(req.session.user, ["mastercorporate", "corporate", "developer", "admin"])
) { ) {
return res.status(401).json({ error: "Unauthorized" }); return res.status(403).json({ error: "Unauthorized" });
} }
const { ids, startDate, endDate, searchText } = req.body as { const {
ids,
startDate,
endDate,
searchText,
displaySelection = true,
} = req.body as {
ids: string[]; ids: string[];
startDate?: string; startDate?: string;
endDate?: string; endDate?: string;
searchText: string; searchText: string;
displaySelection?: boolean;
}; };
const startDateParsed = startDate ? new Date(startDate) : undefined; const startDateParsed = startDate ? new Date(startDate) : undefined;
const endDateParsed = endDate ? new Date(endDate) : undefined; const endDateParsed = endDate ? new Date(endDate) : undefined;
@@ -83,7 +90,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
); );
const getGradingSystemHelper = ( const getGradingSystemHelper = (
exams: {id: string; module: Module; assignee: string}[], exams: { id: string; module: Module; assignee: string }[],
assigner: string, assigner: string,
user: User, user: User,
correct: number, correct: number,
@@ -100,15 +107,18 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
"level", "level",
user.focus user.focus
); );
return { label: getGradingLabel(bandScore, gradingSystem?.steps || []), score: bandScore }; return {
label: getGradingLabel(bandScore, gradingSystem?.steps || []),
score: bandScore,
};
} }
} }
return { score: -1, label: "N/A" }; return { score: -1, label: "N/A" };
}; };
const tableResults = assignments.reduce( const tableResults = assignments
(accmA: TableData[], a: AssignmentWithCorporateId) => { .reduce((accmA: TableData[], a: AssignmentWithCorporateId) => {
const userResults = a.assignees.map((assignee) => { const userResults = a.assignees.map((assignee) => {
const userStats = const userStats =
a.results.find((r) => r.user === assignee)?.stats || []; a.results.find((r) => r.user === assignee)?.stats || [];
@@ -124,7 +134,6 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
total total
); );
console.log("Level", level); console.log("Level", level);
const commonData = { const commonData = {
user: userData?.name || "", user: userData?.name || "",
@@ -145,12 +154,14 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
}; };
} }
const partsData = userStats.every((e) => e.module === "level") ? userStats.reduce((acc, e, index) => { const partsData = userStats.every((e) => e.module === "level")
? userStats.reduce((acc, e, index) => {
return { return {
...acc, ...acc,
[`part${index}`]: `${e.score.correct}/${e.score.total}` [`part${index}`]: `${e.score.correct}/${e.score.total}`,
} };
}, {}) : {}; }, {})
: {};
return { return {
...commonData, ...commonData,
@@ -162,9 +173,8 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
}) as TableData[]; }) as TableData[];
return [...accmA, ...userResults]; return [...accmA, ...userResults];
}, }, [])
[] .sort((a, b) => b.score - a.score);
).sort((a,b) => b.score - a.score);
// Create a new workbook and add a worksheet // Create a new workbook and add a worksheet
const workbook = new ExcelJS.Workbook(); const workbook = new ExcelJS.Workbook();
@@ -179,10 +189,14 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
label: "Email", label: "Email",
value: (entry: TableData) => entry.email, value: (entry: TableData) => entry.email,
}, },
...(displaySelection
? [
{ {
label: "Corporate", label: "Corporate",
value: (entry: TableData) => entry.corporate, value: (entry: TableData) => entry.corporate,
}, },
]
: []),
{ {
label: "Assignment", label: "Assignment",
value: (entry: TableData) => entry.assignment, value: (entry: TableData) => entry.assignment,