Updated Master Statistical
This commit is contained in:
@@ -625,7 +625,8 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
<h2 className="text-2xl font-semibold">Master Statistical</h2>
|
<h2 className="text-2xl font-semibold">Master Statistical</h2>
|
||||||
</div>
|
</div>
|
||||||
<MasterStatistical
|
<MasterStatistical
|
||||||
users={masterCorporateUserGroups.reduce((accm: CorporateUser[], id) => {
|
users={users}
|
||||||
|
corporateUsers={masterCorporateUserGroups.reduce((accm: CorporateUser[], id) => {
|
||||||
const user = users.find((u) => u.id === id) as CorporateUser;
|
const user = users.find((u) => u.id === id) as CorporateUser;
|
||||||
if (user) return [...accm, user];
|
if (user) return [...accm, user];
|
||||||
return accm;
|
return accm;
|
||||||
@@ -694,13 +695,13 @@ export default function MasterCorporateDashboard({user}: Props) {
|
|||||||
color="purple"
|
color="purple"
|
||||||
onClick={() => setPage("studentsPerformance")}
|
onClick={() => setPage("studentsPerformance")}
|
||||||
/>
|
/>
|
||||||
{/* <IconCard
|
<IconCard
|
||||||
Icon={BsDatabase}
|
Icon={BsDatabase}
|
||||||
label="Master Statistical"
|
label="Master Statistical"
|
||||||
// value={masterCorporateUserGroups.length}
|
// value={masterCorporateUserGroups.length}
|
||||||
color="purple"
|
color="purple"
|
||||||
onClick={() => setPage("statistical")}
|
onClick={() => setPage("statistical")}
|
||||||
/> */}
|
/>
|
||||||
<button
|
<button
|
||||||
disabled={isAssignmentsLoading}
|
disabled={isAssignmentsLoading}
|
||||||
onClick={() => setPage("assignments")}
|
onClick={() => setPage("assignments")}
|
||||||
|
|||||||
@@ -1,22 +1,70 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {CorporateUser} from "@/interfaces/user";
|
import { CorporateUser, User } from "@/interfaces/user";
|
||||||
import { BsBank, BsPersonFill } from "react-icons/bs";
|
import { BsBank, BsPersonFill } from "react-icons/bs";
|
||||||
import IconCard from "./IconCard";
|
import IconCard from "./IconCard";
|
||||||
import useAssignmentsCorporates from "@/hooks/useAssignmentCorporates";
|
import useAssignmentsCorporates from "@/hooks/useAssignmentCorporates";
|
||||||
|
import ReactDatePicker from "react-datepicker";
|
||||||
|
import moment from "moment";
|
||||||
|
import { groupBySession } from "@/utils/stats";
|
||||||
|
import { Assignment, AssignmentResult } from "@/interfaces/results";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
users: CorporateUser[];
|
corporateUsers: CorporateUser[];
|
||||||
|
users: User[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TableData {
|
||||||
|
user: string;
|
||||||
|
correct: number;
|
||||||
|
corporate: string;
|
||||||
|
submitted: boolean;
|
||||||
|
date: moment.Moment;
|
||||||
|
assignment: string;
|
||||||
}
|
}
|
||||||
const MasterStatistical = (props: Props) => {
|
const MasterStatistical = (props: Props) => {
|
||||||
const {users} = props;
|
const { users, corporateUsers } = props;
|
||||||
|
|
||||||
const usersList = React.useMemo(() => users.map((x) => x.id), [users]);
|
const corporates = React.useMemo(() => corporateUsers.map((x) => x.id), [corporateUsers]);
|
||||||
|
const [startDate, setStartDate] = React.useState<Date | null>(
|
||||||
|
moment("01/01/2023").toDate()
|
||||||
|
);
|
||||||
|
const [endDate, setEndDate] = React.useState<Date | null>(
|
||||||
|
moment().endOf("year").toDate()
|
||||||
|
);
|
||||||
|
|
||||||
const {assignments} = useAssignmentsCorporates({corporates: usersList});
|
const { assignments } = useAssignmentsCorporates({
|
||||||
|
corporates,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const x = assignments.reduce((accmA: TableData[], a: Assignment) => {
|
||||||
|
const userResults = a.results.reduce((accmB: TableData[], r: AssignmentResult) => {
|
||||||
|
const userStats = groupBySession(r.stats);
|
||||||
|
const data = Object.keys(userStats).map((key) => ({
|
||||||
|
user: users.find((u) => u.id === r.user)?.name || "",
|
||||||
|
correct: userStats[key].reduce((n, e) => n + e.score.correct, 0),
|
||||||
|
corporate: users.find((u) => u.id === a.assigner)?.name || "",
|
||||||
|
submitted: false,
|
||||||
|
date: moment.max(userStats[key].map((e) => moment(e.date))),
|
||||||
|
assignment: a.name,
|
||||||
|
}));
|
||||||
|
return [...accmB, ...data];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return [...accmA, ...userResults];
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-wrap gap-2 items-center text-center">
|
<div className="flex flex-wrap gap-2 items-center text-center">
|
||||||
<IconCard Icon={BsBank} label="Consolidate" value={0} color="purple" onClick={() => console.log("clicked")} />
|
<IconCard
|
||||||
{users.map((group) => (
|
Icon={BsBank}
|
||||||
|
label="Consolidate"
|
||||||
|
value={0}
|
||||||
|
color="purple"
|
||||||
|
onClick={() => console.log("clicked")}
|
||||||
|
/>
|
||||||
|
{corporateUsers.map((group) => (
|
||||||
<IconCard
|
<IconCard
|
||||||
key={group.id}
|
key={group.id}
|
||||||
Icon={BsBank}
|
Icon={BsBank}
|
||||||
@@ -26,7 +74,33 @@ const MasterStatistical = (props: Props) => {
|
|||||||
onClick={() => console.log("clicked", group)}
|
onClick={() => console.log("clicked", group)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<IconCard onClick={() => console.log("clicked")} Icon={BsPersonFill} label="Consolidate Highest Student" color="purple" />
|
|
||||||
|
<ReactDatePicker
|
||||||
|
dateFormat="dd/MM/yyyy"
|
||||||
|
className="px-4 py-6 w-full text-sm text-center font-normal placeholder:text-mti-gray-cool disabled:bg-mti-gray-platinum/40 disabled:text-mti-gray-dim disabled:cursor-not-allowed rounded-full border border-mti-gray-platinum focus:outline-none"
|
||||||
|
selected={startDate}
|
||||||
|
startDate={startDate}
|
||||||
|
endDate={endDate}
|
||||||
|
selectsRange
|
||||||
|
showMonthDropdown
|
||||||
|
onChange={([initialDate, finalDate]: [Date, Date]) => {
|
||||||
|
setStartDate(initialDate ?? moment("01/01/2023").toDate());
|
||||||
|
if (finalDate) {
|
||||||
|
// basicly selecting a final day works as if I'm selecting the first
|
||||||
|
// minute of that day. this way it covers the whole day
|
||||||
|
setEndDate(moment(finalDate).endOf("day").toDate());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setEndDate(null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<IconCard
|
||||||
|
onClick={() => console.log("clicked")}
|
||||||
|
Icon={BsPersonFill}
|
||||||
|
label="Consolidate Highest Student"
|
||||||
|
color="purple"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import { Assignment } from "@/interfaces/results";
|
import { Assignment } from "@/interfaces/results";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import moment from "moment";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function useAssignmentsCorporates({
|
export default function useAssignmentsCorporates({
|
||||||
corporates,
|
corporates,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
}: {
|
}: {
|
||||||
corporates: string[];
|
corporates: string[];
|
||||||
|
startDate: Date | null;
|
||||||
|
endDate: Date | null;
|
||||||
}) {
|
}) {
|
||||||
const [assignments, setAssignments] = useState<Assignment[]>([]);
|
const [assignments, setAssignments] = useState<Assignment[]>([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@@ -18,9 +23,15 @@ export default function useAssignmentsCorporates({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
const urlSearchParams = new URLSearchParams({
|
||||||
|
ids: corporates.join(","),
|
||||||
|
...(startDate ? { startDate: startDate.toISOString() } : {}),
|
||||||
|
...(endDate ? { endDate: endDate.toISOString() } : {}),
|
||||||
|
});
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.get<Assignment[]>(
|
.get<Assignment[]>(
|
||||||
`/api/assignments/corporate?ids=${corporates.join(",")}`
|
`/api/assignments/corporate?${urlSearchParams.toString()}`
|
||||||
)
|
)
|
||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
setAssignments(response.data);
|
setAssignments(response.data);
|
||||||
@@ -28,7 +39,7 @@ export default function useAssignmentsCorporates({
|
|||||||
.finally(() => setIsLoading(false));
|
.finally(() => setIsLoading(false));
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(getData, [corporates]);
|
useEffect(getData, [corporates, startDate, endDate]);
|
||||||
|
|
||||||
return { assignments, isLoading, isError, reload: getData };
|
return { assignments, isLoading, isError, reload: getData };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,16 +10,18 @@ interface ModuleResult {
|
|||||||
total: number;
|
total: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AssignmentResult {
|
||||||
|
user: string;
|
||||||
|
type: "academic" | "general";
|
||||||
|
stats: Stat[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface Assignment {
|
export interface Assignment {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
assigner: string;
|
assigner: string;
|
||||||
assignees: string[];
|
assignees: string[];
|
||||||
results: {
|
results: AssignmentResult[];
|
||||||
user: string;
|
|
||||||
type: "academic" | "general";
|
|
||||||
stats: Stat[];
|
|
||||||
}[];
|
|
||||||
exams: {id: string; module: Module; assignee: string}[];
|
exams: {id: string; module: Module; assignee: string}[];
|
||||||
instructorGender?: InstructorGender;
|
instructorGender?: InstructorGender;
|
||||||
startDate: Date;
|
startDate: Date;
|
||||||
|
|||||||
@@ -20,13 +20,16 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function GET(req: NextApiRequest, res: NextApiResponse) {
|
async function GET(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const { ids } = req.query as { ids: string };
|
const { ids, startDate, endDate } = req.query as { ids: string, startDate?: string, endDate?: string };
|
||||||
|
|
||||||
|
const startDateParsed = startDate ? new Date(startDate) : undefined;
|
||||||
|
const endDateParsed = endDate ? new Date(endDate) : undefined;
|
||||||
try {
|
try {
|
||||||
const idsList = ids.split(",");
|
const idsList = ids.split(",");
|
||||||
|
|
||||||
const assigners = await Promise.all(idsList.map(getAllAssignersByCorporate));
|
const assigners = await Promise.all(idsList.map(getAllAssignersByCorporate));
|
||||||
const assignmentList = [...assigners.flat(), ...idsList];
|
const assignmentList = [...assigners.flat(), ...idsList];
|
||||||
const assignments = await getAssignmentsByAssigners(assignmentList);
|
const assignments = await getAssignmentsByAssigners(assignmentList, startDateParsed, endDateParsed);
|
||||||
res.status(200).json(assignments);
|
res.status(200).json(assignments);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
res.status(500).json({ error: err.message });
|
res.status(500).json({ error: err.message });
|
||||||
|
|||||||
@@ -1,19 +1,58 @@
|
|||||||
import { app } from "@/firebase";
|
import { app } from "@/firebase";
|
||||||
import { Assignment } from "@/interfaces/results";
|
import { Assignment } from "@/interfaces/results";
|
||||||
import {collection, getDocs, getFirestore, query, where} from "firebase/firestore";
|
import {
|
||||||
|
collection,
|
||||||
|
getDocs,
|
||||||
|
getFirestore,
|
||||||
|
query,
|
||||||
|
where,
|
||||||
|
} from "firebase/firestore";
|
||||||
|
|
||||||
const db = getFirestore(app);
|
const db = getFirestore(app);
|
||||||
|
|
||||||
export const getAssignmentsByAssigner = async (id: string) => {
|
export const getAssignmentsByAssigner = async (
|
||||||
const {docs} = await getDocs(query(collection(db, "assignments"), where("assigner", "==", id)));
|
id: string,
|
||||||
|
startDate?: Date,
|
||||||
|
endDate?: Date
|
||||||
|
) => {
|
||||||
|
const { docs } = await getDocs(
|
||||||
|
query(
|
||||||
|
collection(db, "assignments"),
|
||||||
|
...[
|
||||||
|
where("assigner", "==", id),
|
||||||
|
...(startDate ? [where("startDate", ">=", startDate.toISOString())] : []),
|
||||||
|
// firebase doesnt accept compound queries so we have to filter on the server
|
||||||
|
// ...endDate ? [where("endDate", "<=", endDate)] : [],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (endDate) {
|
||||||
|
return docs
|
||||||
|
.map((x) => ({ ...(x.data() as Assignment), id: x.id }))
|
||||||
|
.filter((x) => new Date(x.endDate) <= endDate) as Assignment[];
|
||||||
|
}
|
||||||
return docs.map((x) => ({ ...x.data(), id: x.id })) as Assignment[];
|
return docs.map((x) => ({ ...x.data(), id: x.id })) as Assignment[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAssignmentsByAssignerBetweenDates = async (id: string, startDate: Date, endDate: Date) => {
|
export const getAssignmentsByAssignerBetweenDates = async (
|
||||||
const {docs} = await getDocs(query(collection(db, "assignments"), where("assigner", "==", id), ));
|
id: string,
|
||||||
|
startDate: Date,
|
||||||
|
endDate: Date
|
||||||
|
) => {
|
||||||
|
const { docs } = await getDocs(
|
||||||
|
query(collection(db, "assignments"), where("assigner", "==", id))
|
||||||
|
);
|
||||||
return docs.map((x) => ({ ...x.data(), id: x.id })) as Assignment[];
|
return docs.map((x) => ({ ...x.data(), id: x.id })) as Assignment[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAssignmentsByAssigners = async (ids: string[]) => {
|
export const getAssignmentsByAssigners = async (
|
||||||
return (await Promise.all(ids.map(getAssignmentsByAssigner))).flat();
|
ids: string[],
|
||||||
|
startDate?: Date,
|
||||||
|
endDate?: Date
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
await Promise.all(
|
||||||
|
ids.map((id) => getAssignmentsByAssigner(id, startDate, endDate))
|
||||||
|
)
|
||||||
|
).flat();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user