Merge branch 'develop' into ENCOA-83_MasterStatistical
This commit is contained in:
@@ -29,7 +29,7 @@ import {
|
||||
} from "react-icons/bs";
|
||||
import UserCard from "@/components/UserCard";
|
||||
import useGroups from "@/hooks/useGroups";
|
||||
import {calculateAverageLevel, calculateBandScore} from "@/utils/score";
|
||||
import {averageLevelCalculator, calculateAverageLevel, calculateBandScore} from "@/utils/score";
|
||||
import {MODULE_ARRAY} from "@/utils/moduleUtils";
|
||||
import {Module} from "@/interfaces";
|
||||
import {groupByExam} from "@/utils/stats";
|
||||
@@ -45,11 +45,113 @@ import AssignmentView from "./AssignmentView";
|
||||
import AssignmentCreator from "./AssignmentCreator";
|
||||
import clsx from "clsx";
|
||||
import AssignmentCard from "./AssignmentCard";
|
||||
import {createColumnHelper} from "@tanstack/react-table";
|
||||
import Checkbox from "@/components/Low/Checkbox";
|
||||
import List from "@/components/List";
|
||||
import {getUserCompanyName} from "@/resources/user";
|
||||
|
||||
interface Props {
|
||||
user: CorporateUser;
|
||||
}
|
||||
|
||||
type StudentPerformanceItem = User & {corporateName: string; group: string};
|
||||
const StudentPerformanceList = ({items, stats, users}: {items: StudentPerformanceItem[]; stats: Stat[]; users: User[]}) => {
|
||||
const [isShowingAmount, setIsShowingAmount] = useState(false);
|
||||
|
||||
const columnHelper = createColumnHelper<StudentPerformanceItem>();
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor("name", {
|
||||
header: "Student Name",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
columnHelper.accessor("email", {
|
||||
header: "E-mail",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
columnHelper.accessor("demographicInformation.passport_id", {
|
||||
header: "ID",
|
||||
cell: (info) => info.getValue() || "N/A",
|
||||
}),
|
||||
columnHelper.accessor("group", {
|
||||
header: "Group",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
columnHelper.accessor("corporateName", {
|
||||
header: "Corporate",
|
||||
cell: (info) => info.getValue() || "N/A",
|
||||
}),
|
||||
columnHelper.accessor("levels.reading", {
|
||||
header: "Reading",
|
||||
cell: (info) =>
|
||||
!isShowingAmount
|
||||
? info.getValue() || 0
|
||||
: `${Object.keys(groupByExam(stats.filter((x) => x.module === "reading" && x.user === info.row.original.id))).length} exams`,
|
||||
}),
|
||||
columnHelper.accessor("levels.listening", {
|
||||
header: "Listening",
|
||||
cell: (info) =>
|
||||
!isShowingAmount
|
||||
? info.getValue() || 0
|
||||
: `${Object.keys(groupByExam(stats.filter((x) => x.module === "listening" && x.user === info.row.original.id))).length} exams`,
|
||||
}),
|
||||
columnHelper.accessor("levels.writing", {
|
||||
header: "Writing",
|
||||
cell: (info) =>
|
||||
!isShowingAmount
|
||||
? info.getValue() || 0
|
||||
: `${Object.keys(groupByExam(stats.filter((x) => x.module === "writing" && x.user === info.row.original.id))).length} exams`,
|
||||
}),
|
||||
columnHelper.accessor("levels.speaking", {
|
||||
header: "Speaking",
|
||||
cell: (info) =>
|
||||
!isShowingAmount
|
||||
? info.getValue() || 0
|
||||
: `${Object.keys(groupByExam(stats.filter((x) => x.module === "speaking" && x.user === info.row.original.id))).length} exams`,
|
||||
}),
|
||||
columnHelper.accessor("levels.level", {
|
||||
header: "Level",
|
||||
cell: (info) =>
|
||||
!isShowingAmount
|
||||
? info.getValue() || 0
|
||||
: `${Object.keys(groupByExam(stats.filter((x) => x.module === "level" && x.user === info.row.original.id))).length} exams`,
|
||||
}),
|
||||
columnHelper.accessor("levels", {
|
||||
id: "overall_level",
|
||||
header: "Overall",
|
||||
cell: (info) =>
|
||||
!isShowingAmount
|
||||
? averageLevelCalculator(
|
||||
users,
|
||||
stats.filter((x) => x.user === info.row.original.id),
|
||||
).toFixed(1)
|
||||
: `${Object.keys(groupByExam(stats.filter((x) => x.user === info.row.original.id))).length} exams`,
|
||||
}),
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 w-full h-full">
|
||||
<Checkbox isChecked={isShowingAmount} onChange={setIsShowingAmount}>
|
||||
Show Utilization
|
||||
</Checkbox>
|
||||
<List<StudentPerformanceItem>
|
||||
data={items.sort(
|
||||
(a, b) =>
|
||||
averageLevelCalculator(
|
||||
users,
|
||||
stats.filter((x) => x.user === b.id),
|
||||
) -
|
||||
averageLevelCalculator(
|
||||
users,
|
||||
stats.filter((x) => x.user === a.id),
|
||||
),
|
||||
)}
|
||||
columns={columns}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function CorporateDashboard({user}: Props) {
|
||||
const [page, setPage] = useState("");
|
||||
const [selectedUser, setSelectedUser] = useState<User>();
|
||||
@@ -57,11 +159,12 @@ export default function CorporateDashboard({user}: Props) {
|
||||
const [corporateUserToShow, setCorporateUserToShow] = useState<CorporateUser>();
|
||||
const [selectedAssignment, setSelectedAssignment] = useState<Assignment>();
|
||||
const [isCreatingAssignment, setIsCreatingAssignment] = useState(false);
|
||||
const [userBalance, setUserBalance] = useState(0);
|
||||
|
||||
const {stats} = useStats();
|
||||
const {users, reload} = useUsers();
|
||||
const {users, reload, isLoading} = useUsers();
|
||||
const {codes} = useCodes(user.id);
|
||||
const {groups} = useGroups(user.id);
|
||||
const {groups} = useGroups({admin: user.id});
|
||||
const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({corporate: user.id});
|
||||
|
||||
const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
|
||||
@@ -71,6 +174,14 @@ export default function CorporateDashboard({user}: Props) {
|
||||
setShowModal(!!selectedUser && page === "");
|
||||
}, [selectedUser, page]);
|
||||
|
||||
useEffect(() => {
|
||||
const relatedGroups = groups.filter((x) => x.name === "Students" || x.name === "Teachers" || x.name === "Corporate");
|
||||
const usersInGroups = relatedGroups.map((x) => x.participants).flat();
|
||||
const filteredCodes = codes.filter((x) => !x.userId || !usersInGroups.includes(x.userId));
|
||||
|
||||
setUserBalance(usersInGroups.length + filteredCodes.length);
|
||||
}, [codes, groups]);
|
||||
|
||||
useEffect(() => {
|
||||
// in this case it fetches the master corporate account
|
||||
getUserCorporate(user.id).then(setCorporateUserToShow);
|
||||
@@ -228,7 +339,7 @@ export default function CorporateDashboard({user}: Props) {
|
||||
<h2 className="text-2xl font-semibold">Active Assignments ({assignments.filter(activeFilter).length})</h2>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{assignments.filter(activeFilter).map((a) => (
|
||||
<AssignmentCard {...a} onClick={() => setSelectedAssignment(a)} key={a.id} />
|
||||
<AssignmentCard {...a} users={users} onClick={() => setSelectedAssignment(a)} key={a.id} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
@@ -244,6 +355,7 @@ export default function CorporateDashboard({user}: Props) {
|
||||
{assignments.filter(futureFilter).map((a) => (
|
||||
<AssignmentCard
|
||||
{...a}
|
||||
users={users}
|
||||
onClick={() => {
|
||||
setSelectedAssignment(a);
|
||||
setIsCreatingAssignment(true);
|
||||
@@ -259,6 +371,7 @@ export default function CorporateDashboard({user}: Props) {
|
||||
{assignments.filter(pastFilter).map((a) => (
|
||||
<AssignmentCard
|
||||
{...a}
|
||||
users={users}
|
||||
onClick={() => setSelectedAssignment(a)}
|
||||
key={a.id}
|
||||
allowDownload
|
||||
@@ -275,6 +388,7 @@ export default function CorporateDashboard({user}: Props) {
|
||||
{assignments.filter(archivedFilter).map((a) => (
|
||||
<AssignmentCard
|
||||
{...a}
|
||||
users={users}
|
||||
onClick={() => setSelectedAssignment(a)}
|
||||
key={a.id}
|
||||
allowDownload
|
||||
@@ -289,6 +403,36 @@ export default function CorporateDashboard({user}: Props) {
|
||||
);
|
||||
};
|
||||
|
||||
const StudentPerformancePage = () => {
|
||||
const students = users
|
||||
.filter((x) => x.type === "student" && groups.flatMap((g) => g.participants).includes(x.id))
|
||||
.map((u) => ({
|
||||
...u,
|
||||
group: groups.find((x) => x.participants.includes(u.id))?.name || "N/A",
|
||||
corporateName: getUserCompanyName(u, users, groups),
|
||||
}));
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full flex justify-between items-center">
|
||||
<div
|
||||
onClick={() => setPage("")}
|
||||
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>
|
||||
<div
|
||||
onClick={reload}
|
||||
className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
|
||||
<span>Reload</span>
|
||||
<BsArrowRepeat className={clsx("text-xl", isLoading && "animate-spin")} />
|
||||
</div>
|
||||
</div>
|
||||
<StudentPerformanceList items={students} stats={stats} users={users} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const averageLevelCalculator = (studentStats: Stat[]) => {
|
||||
const formattedStats = studentStats
|
||||
.map((s) => ({
|
||||
@@ -352,7 +496,7 @@ export default function CorporateDashboard({user}: Props) {
|
||||
<IconCard
|
||||
Icon={BsPersonCheck}
|
||||
label="User Balance"
|
||||
value={`${codes.length}/${user.corporateInformation?.companyInformation?.userAmount || 0}`}
|
||||
value={`${userBalance}/${user.corporateInformation?.companyInformation?.userAmount || 0}`}
|
||||
color="purple"
|
||||
/>
|
||||
<IconCard
|
||||
@@ -361,6 +505,13 @@ export default function CorporateDashboard({user}: Props) {
|
||||
value={user.subscriptionExpirationDate ? moment(user.subscriptionExpirationDate).format("DD/MM/yyyy") : "Unlimited"}
|
||||
color="rose"
|
||||
/>
|
||||
<IconCard
|
||||
Icon={BsPersonFillGear}
|
||||
label="Student Performance"
|
||||
value={users.filter(studentFilter).length}
|
||||
color="purple"
|
||||
onClick={() => setPage("studentsPerformance")}
|
||||
/>
|
||||
<button
|
||||
disabled={isAssignmentsLoading}
|
||||
onClick={() => setPage("assignments")}
|
||||
@@ -489,6 +640,7 @@ export default function CorporateDashboard({user}: Props) {
|
||||
{page === "teachers" && <TeachersList />}
|
||||
{page === "groups" && <GroupsList />}
|
||||
{page === "assignments" && <AssignmentsPage />}
|
||||
{page === "studentsPerformance" && <StudentPerformancePage />}
|
||||
{page === "" && <DefaultDashboard />}
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user