diff --git a/src/dashboards/AssignmentCard.tsx b/src/dashboards/AssignmentCard.tsx
index 7316c379..1886cedd 100644
--- a/src/dashboards/AssignmentCard.tsx
+++ b/src/dashboards/AssignmentCard.tsx
@@ -10,6 +10,7 @@ import {usePDFDownload} from "@/hooks/usePDFDownload";
import {useAssignmentArchive} from "@/hooks/useAssignmentArchive";
import {uniqBy} from "lodash";
import {useAssignmentUnarchive} from "@/hooks/useAssignmentUnarchive";
+import {getUserName} from "@/utils/users";
interface Props {
onClick?: () => void;
@@ -35,6 +36,8 @@ export default function AssignmentCard({
allowArchive,
allowUnarchive,
}: Assignment & Props) {
+ const {users} = useUsers();
+
const renderPdfIcon = usePDFDownload("assignments");
const renderArchiveIcon = useAssignmentArchive(id, reload);
const renderUnarchiveIcon = useAssignmentUnarchive(id, reload);
@@ -72,11 +75,14 @@ export default function AssignmentCard({
textClassName={results.length / assignees.length < 0.5 ? "!text-mti-gray-dim font-light" : "text-white"}
/>
-
- {moment(startDate).format("DD/MM/YY, HH:mm")}
- -
- {moment(endDate).format("DD/MM/YY, HH:mm")}
-
+
+
+ {moment(startDate).format("DD/MM/YY, HH:mm")}
+ -
+ {moment(endDate).format("DD/MM/YY, HH:mm")}
+
+ Assigner: {getUserName(users.find((x) => x.id === assigner))}
+
{uniqBy(exams, (x) => x.module).map(({module}) => (
Start Date: {moment(assignment?.startDate).format("DD/MM/YY, HH:mm")}
End Date: {moment(assignment?.endDate).format("DD/MM/YY, HH:mm")}
-
- Assignees:{" "}
- {users
- .filter((u) => assignment?.assignees.includes(u.id))
- .map((u) => `${u.name} (${u.email})`)
- .join(", ")}
-
+
+
+ Assignees:{" "}
+ {users
+ .filter((u) => assignment?.assignees.includes(u.id))
+ .map((u) => `${u.name} (${u.email})`)
+ .join(", ")}
+
+ Assigner: {getUserName(users.find((x) => x.id === assignment?.assigner))}
+
Average Scores
diff --git a/src/dashboards/Corporate.tsx b/src/dashboards/Corporate.tsx
index 06feae46..be87e851 100644
--- a/src/dashboards/Corporate.tsx
+++ b/src/dashboards/Corporate.tsx
@@ -2,412 +2,492 @@
import Modal from "@/components/Modal";
import useStats from "@/hooks/useStats";
import useUsers from "@/hooks/useUsers";
-import { CorporateUser, Group, Stat, User } from "@/interfaces/user";
+import {CorporateUser, Group, Stat, User} from "@/interfaces/user";
import UserList from "@/pages/(admin)/Lists/UserList";
-import { dateSorter } from "@/utils";
+import {dateSorter} from "@/utils";
import moment from "moment";
-import { useEffect, useState } from "react";
+import {useEffect, useState} from "react";
import {
- BsArrowLeft,
- BsClipboard2Data,
- BsClipboard2DataFill,
- BsClock,
- BsGlobeCentralSouthAsia,
- BsPaperclip,
- BsPerson,
- BsPersonAdd,
- BsPersonFill,
- BsPersonFillGear,
- BsPersonGear,
- BsPencilSquare,
- BsPersonBadge,
- BsPersonCheck,
- BsPeople,
+ BsArrowLeft,
+ BsClipboard2Data,
+ BsClipboard2DataFill,
+ BsClock,
+ BsGlobeCentralSouthAsia,
+ BsPaperclip,
+ BsPerson,
+ BsPersonAdd,
+ BsPersonFill,
+ BsPersonFillGear,
+ BsPersonGear,
+ BsPencilSquare,
+ BsPersonBadge,
+ BsPersonCheck,
+ BsPeople,
+ BsArrowRepeat,
+ BsPlus,
+ BsEnvelopePaper,
} from "react-icons/bs";
import UserCard from "@/components/UserCard";
import useGroups from "@/hooks/useGroups";
-import { calculateAverageLevel, calculateBandScore } from "@/utils/score";
-import { MODULE_ARRAY } from "@/utils/moduleUtils";
-import { Module } from "@/interfaces";
-import { groupByExam } from "@/utils/stats";
+import {calculateAverageLevel, calculateBandScore} from "@/utils/score";
+import {MODULE_ARRAY} from "@/utils/moduleUtils";
+import {Module} from "@/interfaces";
+import {groupByExam} from "@/utils/stats";
import IconCard from "./IconCard";
import GroupList from "@/pages/(admin)/Lists/GroupList";
import useFilterStore from "@/stores/listFilterStore";
-import { useRouter } from "next/router";
+import {useRouter} from "next/router";
import useCodes from "@/hooks/useCodes";
-import { getUserCorporate } from "@/utils/groups";
+import {getUserCorporate} from "@/utils/groups";
+import useAssignments from "@/hooks/useAssignments";
+import {Assignment} from "@/interfaces/results";
+import AssignmentView from "./AssignmentView";
+import AssignmentCreator from "./AssignmentCreator";
+import clsx from "clsx";
+import AssignmentCard from "./AssignmentCard";
interface Props {
- user: CorporateUser;
+ user: CorporateUser;
}
-export default function CorporateDashboard({ user }: Props) {
- const [page, setPage] = useState("");
- const [selectedUser, setSelectedUser] = useState
();
- const [showModal, setShowModal] = useState(false);
- const [corporateUserToShow, setCorporateUserToShow] =
- useState();
+export default function CorporateDashboard({user}: Props) {
+ const [page, setPage] = useState("");
+ const [selectedUser, setSelectedUser] = useState();
+ const [showModal, setShowModal] = useState(false);
+ const [corporateUserToShow, setCorporateUserToShow] = useState();
+ const [selectedAssignment, setSelectedAssignment] = useState();
+ const [isCreatingAssignment, setIsCreatingAssignment] = useState(false);
- const { stats } = useStats();
- const { users, reload } = useUsers();
- const { codes } = useCodes(user.id);
- const { groups } = useGroups(user.id);
+ const {stats} = useStats();
+ const {users, reload} = useUsers();
+ const {codes} = useCodes(user.id);
+ const {groups} = useGroups(user.id);
+ const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({corporate: user.id});
- const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
- const router = useRouter();
+ const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
+ const router = useRouter();
- useEffect(() => {
- setShowModal(!!selectedUser && page === "");
- }, [selectedUser, page]);
+ useEffect(() => {
+ setShowModal(!!selectedUser && page === "");
+ }, [selectedUser, page]);
- useEffect(() => {
- // in this case it fetches the master corporate account
- getUserCorporate(user.id).then(setCorporateUserToShow);
- }, [user]);
+ useEffect(() => {
+ // in this case it fetches the master corporate account
+ getUserCorporate(user.id).then(setCorporateUserToShow);
+ }, [user]);
- const studentFilter = (user: User) =>
- user.type === "student" &&
- groups.flatMap((g) => g.participants).includes(user.id);
- const teacherFilter = (user: User) =>
- user.type === "teacher" &&
- groups.flatMap((g) => g.participants).includes(user.id);
+ const studentFilter = (user: User) => user.type === "student" && groups.flatMap((g) => g.participants).includes(user.id);
+ const teacherFilter = (user: User) => user.type === "teacher" && groups.flatMap((g) => g.participants).includes(user.id);
- const getStatsByStudent = (user: User) =>
- stats.filter((s) => s.user === user.id);
+ const getStatsByStudent = (user: User) => stats.filter((s) => s.user === user.id);
- const UserDisplay = (displayUser: User) => (
- setSelectedUser(displayUser)}
- className="flex w-full p-4 gap-4 items-center hover:bg-mti-purple-ultralight cursor-pointer transition ease-in-out duration-300"
- >
-

-
- {displayUser.name}
- {displayUser.email}
-
-
- );
+ const UserDisplay = (displayUser: User) => (
+ setSelectedUser(displayUser)}
+ className="flex w-full p-4 gap-4 items-center hover:bg-mti-purple-ultralight cursor-pointer transition ease-in-out duration-300">
+

+
+ {displayUser.name}
+ {displayUser.email}
+
+
+ );
- const StudentsList = () => {
- const filter = (x: User) =>
- x.type === "student" &&
- (!!selectedUser
- ? groups
- .filter((g) => g.admin === selectedUser.id)
- .flatMap((g) => g.participants)
- .includes(x.id) || false
- : groups.flatMap((g) => g.participants).includes(x.id));
+ const StudentsList = () => {
+ const filter = (x: User) =>
+ x.type === "student" &&
+ (!!selectedUser
+ ? groups
+ .filter((g) => g.admin === selectedUser.id)
+ .flatMap((g) => g.participants)
+ .includes(x.id) || false
+ : groups.flatMap((g) => g.participants).includes(x.id));
- return (
- (
-
-
setPage("")}
- className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
- >
-
- Back
-
-
Students ({total})
-
- )}
- />
- );
- };
+ return (
+ (
+
+
setPage("")}
+ className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
+
+ Back
+
+
Students ({total})
+
+ )}
+ />
+ );
+ };
- const TeachersList = () => {
- const filter = (x: User) =>
- x.type === "teacher" &&
- (!!selectedUser
- ? groups
- .filter((g) => g.admin === selectedUser.id)
- .flatMap((g) => g.participants)
- .includes(x.id) || false
- : groups.flatMap((g) => g.participants).includes(x.id));
+ const TeachersList = () => {
+ const filter = (x: User) =>
+ x.type === "teacher" &&
+ (!!selectedUser
+ ? groups
+ .filter((g) => g.admin === selectedUser.id)
+ .flatMap((g) => g.participants)
+ .includes(x.id) || false
+ : groups.flatMap((g) => g.participants).includes(x.id));
- return (
- (
-
-
setPage("")}
- className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
- >
-
- Back
-
-
Teachers ({total})
-
- )}
- />
- );
- };
+ return (
+ (
+
+
setPage("")}
+ className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
+
+ Back
+
+
Teachers ({total})
+
+ )}
+ />
+ );
+ };
- const GroupsList = () => {
- const filter = (x: Group) =>
- x.admin === user.id || x.participants.includes(user.id);
+ const GroupsList = () => {
+ const filter = (x: Group) => x.admin === user.id || x.participants.includes(user.id);
- return (
- <>
-
-
setPage("")}
- className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
- >
-
- Back
-
-
- Groups ({groups.filter(filter).length})
-
-
+ return (
+ <>
+
+
setPage("")}
+ className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
+
+ Back
+
+
Groups ({groups.filter(filter).length})
+
-
- >
- );
- };
+
+ >
+ );
+ };
- const averageLevelCalculator = (studentStats: Stat[]) => {
- const formattedStats = studentStats
- .map((s) => ({
- focus: users.find((u) => u.id === s.user)?.focus,
- score: s.score,
- module: s.module,
- }))
- .filter((f) => !!f.focus);
- const bandScores = formattedStats.map((s) => ({
- module: s.module,
- level: calculateBandScore(
- s.score.correct,
- s.score.total,
- s.module,
- s.focus!
- ),
- }));
+ const AssignmentsPage = () => {
+ const activeFilter = (a: Assignment) =>
+ moment(a.endDate).isAfter(moment()) && moment(a.startDate).isBefore(moment()) && a.assignees.length > a.results.length;
+ const pastFilter = (a: Assignment) => (moment(a.endDate).isBefore(moment()) || a.assignees.length === a.results.length) && !a.archived;
+ const archivedFilter = (a: Assignment) => a.archived;
+ const futureFilter = (a: Assignment) => moment(a.startDate).isAfter(moment());
- const levels: { [key in Module]: number } = {
- reading: 0,
- listening: 0,
- writing: 0,
- speaking: 0,
- level: 0,
- };
- bandScores.forEach((b) => (levels[b.module] += b.level));
+ return (
+ <>
+ {
+ setSelectedAssignment(undefined);
+ setIsCreatingAssignment(false);
+ reloadAssignments();
+ }}
+ assignment={selectedAssignment}
+ />
+ x.admin === user.id || x.participants.includes(user.id))}
+ users={users.filter(
+ (x) =>
+ x.type === "student" &&
+ (!!selectedUser
+ ? groups
+ .filter((g) => g.admin === selectedUser.id)
+ .flatMap((g) => g.participants)
+ .includes(x.id) || false
+ : groups.flatMap((g) => g.participants).includes(x.id)),
+ )}
+ assigner={user.id}
+ isCreating={isCreatingAssignment}
+ cancelCreation={() => {
+ setIsCreatingAssignment(false);
+ setSelectedAssignment(undefined);
+ reloadAssignments();
+ }}
+ />
+
+
setPage("")}
+ className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
+
+ Back
+
+
+ Reload
+
+
+
+
+ Active Assignments ({assignments.filter(activeFilter).length})
+
+ {assignments.filter(activeFilter).map((a) => (
+
setSelectedAssignment(a)} key={a.id} />
+ ))}
+
+
+
+ Planned Assignments ({assignments.filter(futureFilter).length})
+
+
setIsCreatingAssignment(true)}
+ className="w-[250px] h-[200px] flex flex-col gap-2 items-center justify-center bg-white hover:bg-mti-purple-ultralight text-mti-purple-light hover:text-mti-purple-dark border border-mti-gray-platinum hover:drop-shadow p-4 cursor-pointer rounded-xl transition ease-in-out duration-300">
+
+ New Assignment
+
+ {assignments.filter(futureFilter).map((a) => (
+
{
+ setSelectedAssignment(a);
+ setIsCreatingAssignment(true);
+ }}
+ key={a.id}
+ />
+ ))}
+
+
+
+ Past Assignments ({assignments.filter(pastFilter).length})
+
+ {assignments.filter(pastFilter).map((a) => (
+
setSelectedAssignment(a)}
+ key={a.id}
+ allowDownload
+ reload={reloadAssignments}
+ allowArchive
+ />
+ ))}
+
+
+
+ Archived Assignments ({assignments.filter(archivedFilter).length})
+
+ {assignments.filter(archivedFilter).map((a) => (
+
setSelectedAssignment(a)}
+ key={a.id}
+ allowDownload
+ reload={reloadAssignments}
+ allowUnarchive
+ />
+ ))}
+
+
+ >
+ );
+ };
- return calculateAverageLevel(levels);
- };
+ const averageLevelCalculator = (studentStats: Stat[]) => {
+ const formattedStats = studentStats
+ .map((s) => ({
+ focus: users.find((u) => u.id === s.user)?.focus,
+ score: s.score,
+ module: s.module,
+ }))
+ .filter((f) => !!f.focus);
+ const bandScores = formattedStats.map((s) => ({
+ module: s.module,
+ level: calculateBandScore(s.score.correct, s.score.total, s.module, s.focus!),
+ }));
- const DefaultDashboard = () => (
- <>
- {corporateUserToShow && (
-
- Linked to:{" "}
-
- {corporateUserToShow?.corporateInformation?.companyInformation
- .name || corporateUserToShow.name}
-
-
- )}
-
- setPage("students")}
- Icon={BsPersonFill}
- label="Students"
- value={users.filter(studentFilter).length}
- color="purple"
- />
- setPage("teachers")}
- Icon={BsPencilSquare}
- label="Teachers"
- value={users.filter(teacherFilter).length}
- color="purple"
- />
-
- groups.flatMap((g) => g.participants).includes(s.user)
- ).length
- }
- color="purple"
- />
-
- groups.flatMap((g) => g.participants).includes(s.user)
- )
- ).toFixed(1)}
- color="purple"
- />
- setPage("groups")}
- Icon={BsPeople}
- label="Groups"
- value={groups.length}
- color="purple"
- />
-
-
-
+ const levels: {[key in Module]: number} = {
+ reading: 0,
+ listening: 0,
+ writing: 0,
+ speaking: 0,
+ level: 0,
+ };
+ bandScores.forEach((b) => (levels[b.module] += b.level));
-
-
-
Latest students
-
- {users
- .filter(studentFilter)
- .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
- .map((x) => (
-
- ))}
-
-
-
-
Latest teachers
-
- {users
- .filter(teacherFilter)
- .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
- .map((x) => (
-
- ))}
-
-
-
-
Highest level students
-
- {users
- .filter(studentFilter)
- .sort(
- (a, b) =>
- calculateAverageLevel(b.levels) -
- calculateAverageLevel(a.levels)
- )
- .map((x) => (
-
- ))}
-
-
-
-
Highest exam count students
-
- {users
- .filter(studentFilter)
- .sort(
- (a, b) =>
- Object.keys(groupByExam(getStatsByStudent(b))).length -
- Object.keys(groupByExam(getStatsByStudent(a))).length
- )
- .map((x) => (
-
- ))}
-
-
-
- >
- );
+ return calculateAverageLevel(levels);
+ };
- return (
- <>
- setSelectedUser(undefined)}>
- <>
- {selectedUser && (
-
-
{
- setSelectedUser(undefined);
- if (shouldReload) reload();
- }}
- onViewStudents={
- selectedUser.type === "corporate" ||
- selectedUser.type === "teacher"
- ? () => {
- appendUserFilters({
- id: "view-students",
- filter: (x: User) => x.type === "student",
- });
- appendUserFilters({
- id: "belongs-to-admin",
- filter: (x: User) =>
- groups
- .filter(
- (g) =>
- g.admin === selectedUser.id ||
- g.participants.includes(selectedUser.id)
- )
- .flatMap((g) => g.participants)
- .includes(x.id),
- });
+ const DefaultDashboard = () => (
+ <>
+ {corporateUserToShow && (
+
+ Linked to: {corporateUserToShow?.corporateInformation?.companyInformation.name || corporateUserToShow.name}
+
+ )}
+
+ setPage("students")}
+ Icon={BsPersonFill}
+ label="Students"
+ value={users.filter(studentFilter).length}
+ color="purple"
+ />
+ setPage("teachers")}
+ Icon={BsPencilSquare}
+ label="Teachers"
+ value={users.filter(teacherFilter).length}
+ color="purple"
+ />
+ groups.flatMap((g) => g.participants).includes(s.user)).length}
+ color="purple"
+ />
+ groups.flatMap((g) => g.participants).includes(s.user))).toFixed(1)}
+ color="purple"
+ />
+ setPage("groups")} Icon={BsPeople} label="Groups" value={groups.length} color="purple" />
+
+
+
+
- router.push("/list/users");
- }
- : undefined
- }
- onViewTeachers={
- selectedUser.type === "corporate" ||
- selectedUser.type === "student"
- ? () => {
- appendUserFilters({
- id: "view-teachers",
- filter: (x: User) => x.type === "teacher",
- });
- appendUserFilters({
- id: "belongs-to-admin",
- filter: (x: User) =>
- groups
- .filter(
- (g) =>
- g.admin === selectedUser.id ||
- g.participants.includes(selectedUser.id)
- )
- .flatMap((g) => g.participants)
- .includes(x.id),
- });
+
+
+
Latest students
+
+ {users
+ .filter(studentFilter)
+ .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
+ .map((x) => (
+
+ ))}
+
+
+
+
Latest teachers
+
+ {users
+ .filter(teacherFilter)
+ .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
+ .map((x) => (
+
+ ))}
+
+
+
+
Highest level students
+
+ {users
+ .filter(studentFilter)
+ .sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))
+ .map((x) => (
+
+ ))}
+
+
+
+
Highest exam count students
+
+ {users
+ .filter(studentFilter)
+ .sort(
+ (a, b) =>
+ Object.keys(groupByExam(getStatsByStudent(b))).length - Object.keys(groupByExam(getStatsByStudent(a))).length,
+ )
+ .map((x) => (
+
+ ))}
+
+
+
+ >
+ );
- router.push("/list/users");
- }
- : undefined
- }
- user={selectedUser}
- />
-
- )}
- >
-
- {page === "students" && }
- {page === "teachers" && }
- {page === "groups" && }
- {page === "" && }
- >
- );
+ return (
+ <>
+ setSelectedUser(undefined)}>
+ <>
+ {selectedUser && (
+
+ {
+ setSelectedUser(undefined);
+ if (shouldReload) reload();
+ }}
+ onViewStudents={
+ selectedUser.type === "corporate" || selectedUser.type === "teacher"
+ ? () => {
+ appendUserFilters({
+ id: "view-students",
+ filter: (x: User) => x.type === "student",
+ });
+ appendUserFilters({
+ id: "belongs-to-admin",
+ filter: (x: User) =>
+ groups
+ .filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id))
+ .flatMap((g) => g.participants)
+ .includes(x.id),
+ });
+
+ router.push("/list/users");
+ }
+ : undefined
+ }
+ onViewTeachers={
+ selectedUser.type === "corporate" || selectedUser.type === "student"
+ ? () => {
+ appendUserFilters({
+ id: "view-teachers",
+ filter: (x: User) => x.type === "teacher",
+ });
+ appendUserFilters({
+ id: "belongs-to-admin",
+ filter: (x: User) =>
+ groups
+ .filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id))
+ .flatMap((g) => g.participants)
+ .includes(x.id),
+ });
+
+ router.push("/list/users");
+ }
+ : undefined
+ }
+ user={selectedUser}
+ />
+
+ )}
+ >
+
+ {page === "students" && }
+ {page === "teachers" && }
+ {page === "groups" && }
+ {page === "assignments" && }
+ {page === "" && }
+ >
+ );
}
diff --git a/src/dashboards/MasterCorporate.tsx b/src/dashboards/MasterCorporate.tsx
index 95736597..e5eb1b96 100644
--- a/src/dashboards/MasterCorporate.tsx
+++ b/src/dashboards/MasterCorporate.tsx
@@ -2,423 +2,496 @@
import Modal from "@/components/Modal";
import useStats from "@/hooks/useStats";
import useUsers from "@/hooks/useUsers";
-import { Group, MasterCorporateUser, Stat, User } from "@/interfaces/user";
+import {Group, MasterCorporateUser, Stat, User} from "@/interfaces/user";
import UserList from "@/pages/(admin)/Lists/UserList";
-import { dateSorter } from "@/utils";
+import {dateSorter} from "@/utils";
import moment from "moment";
-import { useEffect, useState } from "react";
+import {useEffect, useState} from "react";
import {
- BsArrowLeft,
- BsClipboard2Data,
- BsClock,
- BsPaperclip,
- BsPersonFill,
- BsPencilSquare,
- BsPersonCheck,
- BsPeople,
- BsBank,
+ BsArrowLeft,
+ BsClipboard2Data,
+ BsClock,
+ BsPaperclip,
+ BsPersonFill,
+ BsPencilSquare,
+ BsPersonCheck,
+ BsPeople,
+ BsBank,
+ BsEnvelopePaper,
+ BsArrowRepeat,
+ BsPlus,
} from "react-icons/bs";
import UserCard from "@/components/UserCard";
import useGroups from "@/hooks/useGroups";
-import { calculateAverageLevel, calculateBandScore } from "@/utils/score";
-import { MODULE_ARRAY } from "@/utils/moduleUtils";
-import { Module } from "@/interfaces";
-import { groupByExam } from "@/utils/stats";
+import {calculateAverageLevel, calculateBandScore} from "@/utils/score";
+import {MODULE_ARRAY} from "@/utils/moduleUtils";
+import {Module} from "@/interfaces";
+import {groupByExam} from "@/utils/stats";
import IconCard from "./IconCard";
import GroupList from "@/pages/(admin)/Lists/GroupList";
import useFilterStore from "@/stores/listFilterStore";
-import { useRouter } from "next/router";
+import {useRouter} from "next/router";
import useCodes from "@/hooks/useCodes";
+import useAssignments from "@/hooks/useAssignments";
+import {Assignment} from "@/interfaces/results";
+import AssignmentView from "./AssignmentView";
+import AssignmentCreator from "./AssignmentCreator";
+import clsx from "clsx";
+import AssignmentCard from "./AssignmentCard";
interface Props {
- user: MasterCorporateUser;
+ user: MasterCorporateUser;
}
-export default function MasterCorporateDashboard({ user }: Props) {
- const [page, setPage] = useState("");
- const [selectedUser, setSelectedUser] = useState();
- const [showModal, setShowModal] = useState(false);
+export default function MasterCorporateDashboard({user}: Props) {
+ const [page, setPage] = useState("");
+ const [selectedUser, setSelectedUser] = useState();
+ const [showModal, setShowModal] = useState(false);
+ const [selectedAssignment, setSelectedAssignment] = useState();
+ const [isCreatingAssignment, setIsCreatingAssignment] = useState(false);
- const { stats } = useStats();
- const { users, reload } = useUsers();
- const { codes } = useCodes(user.id);
- const { groups } = useGroups(user.id, user.type);
+ const {stats} = useStats();
+ const {users, reload} = useUsers();
+ const {codes} = useCodes(user.id);
+ const {groups} = useGroups(user.id, user.type);
- const masterCorporateUserGroups = [
- ...new Set(
- groups.filter((u) => u.admin === user.id).flatMap((g) => g.participants)
- ),
- ];
- const corporateUserGroups = [
- ...new Set(groups.flatMap((g) => g.participants)),
- ];
+ const masterCorporateUserGroups = [...new Set(groups.filter((u) => u.admin === user.id).flatMap((g) => g.participants))];
+ const corporateUserGroups = [...new Set(groups.flatMap((g) => g.participants))];
- const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
- const router = useRouter();
+ const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({corporate: user.id});
- useEffect(() => {
- setShowModal(!!selectedUser && page === "");
- }, [selectedUser, page]);
+ const appendUserFilters = useFilterStore((state) => state.appendUserFilter);
+ const router = useRouter();
- const studentFilter = (user: User) =>
- user.type === "student" && corporateUserGroups.includes(user.id);
- const teacherFilter = (user: User) =>
- user.type === "teacher" && corporateUserGroups.includes(user.id);
+ useEffect(() => {
+ setShowModal(!!selectedUser && page === "");
+ }, [selectedUser, page]);
- const getStatsByStudent = (user: User) =>
- stats.filter((s) => s.user === user.id);
+ const studentFilter = (user: User) => user.type === "student" && corporateUserGroups.includes(user.id);
+ const teacherFilter = (user: User) => user.type === "teacher" && corporateUserGroups.includes(user.id);
- const UserDisplay = (displayUser: User) => (
- setSelectedUser(displayUser)}
- className="flex w-full p-4 gap-4 items-center hover:bg-mti-purple-ultralight cursor-pointer transition ease-in-out duration-300"
- >
-

-
- {displayUser.name}
- {displayUser.email}
-
-
- );
+ const getStatsByStudent = (user: User) => stats.filter((s) => s.user === user.id);
- const StudentsList = () => {
- const filter = (x: User) =>
- x.type === "student" &&
- (!!selectedUser
- ? corporateUserGroups.includes(x.id) || false
- : corporateUserGroups.includes(x.id));
+ const UserDisplay = (displayUser: User) => (
+ setSelectedUser(displayUser)}
+ className="flex w-full p-4 gap-4 items-center hover:bg-mti-purple-ultralight cursor-pointer transition ease-in-out duration-300">
+

+
+ {displayUser.name}
+ {displayUser.email}
+
+
+ );
- return (
- (
-
-
setPage("")}
- className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
- >
-
- Back
-
-
Students ({total})
-
- )}
- />
- );
- };
+ const StudentsList = () => {
+ const filter = (x: User) =>
+ x.type === "student" && (!!selectedUser ? corporateUserGroups.includes(x.id) || false : corporateUserGroups.includes(x.id));
- const TeachersList = () => {
- const filter = (x: User) =>
- x.type === "teacher" &&
- (!!selectedUser
- ? corporateUserGroups.includes(x.id) || false
- : corporateUserGroups.includes(x.id));
+ return (
+ (
+
+
setPage("")}
+ className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
+
+ Back
+
+
Students ({total})
+
+ )}
+ />
+ );
+ };
- return (
- (
-
-
setPage("")}
- className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
- >
-
- Back
-
-
Teachers ({total})
-
- )}
- />
- );
- };
+ const TeachersList = () => {
+ const filter = (x: User) =>
+ x.type === "teacher" && (!!selectedUser ? corporateUserGroups.includes(x.id) || false : corporateUserGroups.includes(x.id));
- const corporateUserFilter = (x: User) =>
- x.type === "corporate" &&
- (!!selectedUser
- ? masterCorporateUserGroups.includes(x.id) || false
- : masterCorporateUserGroups.includes(x.id));
+ return (
+ (
+
+
setPage("")}
+ className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
+
+ Back
+
+
Teachers ({total})
+
+ )}
+ />
+ );
+ };
- const CorporateList = () => {
- return (
- (
-
-
setPage("")}
- className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
- >
-
- Back
-
-
Corporates ({total})
-
- )}
- />
- );
- };
+ const corporateUserFilter = (x: User) =>
+ x.type === "corporate" && (!!selectedUser ? masterCorporateUserGroups.includes(x.id) || false : masterCorporateUserGroups.includes(x.id));
- const GroupsList = () => {
- return (
- <>
-
-
setPage("")}
- className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"
- >
-
- Back
-
-
- Groups ({groups.length})
-
-
+ const CorporateList = () => {
+ return (
+ (
+
+
setPage("")}
+ className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
+
+ Back
+
+
Corporates ({total})
+
+ )}
+ />
+ );
+ };
-
- >
- );
- };
+ const GroupsList = () => {
+ return (
+ <>
+
+
setPage("")}
+ className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
+
+ Back
+
+
Groups ({groups.length})
+
- const averageLevelCalculator = (studentStats: Stat[]) => {
- const formattedStats = studentStats
- .map((s) => ({
- focus: users.find((u) => u.id === s.user)?.focus,
- score: s.score,
- module: s.module,
- }))
- .filter((f) => !!f.focus);
- const bandScores = formattedStats.map((s) => ({
- module: s.module,
- level: calculateBandScore(
- s.score.correct,
- s.score.total,
- s.module,
- s.focus!
- ),
- }));
+
+ >
+ );
+ };
- const levels: { [key in Module]: number } = {
- reading: 0,
- listening: 0,
- writing: 0,
- speaking: 0,
- level: 0,
- };
- bandScores.forEach((b) => (levels[b.module] += b.level));
+ const AssignmentsPage = () => {
+ const activeFilter = (a: Assignment) =>
+ moment(a.endDate).isAfter(moment()) && moment(a.startDate).isBefore(moment()) && a.assignees.length > a.results.length;
+ const pastFilter = (a: Assignment) => (moment(a.endDate).isBefore(moment()) || a.assignees.length === a.results.length) && !a.archived;
+ const archivedFilter = (a: Assignment) => a.archived;
+ const futureFilter = (a: Assignment) => moment(a.startDate).isAfter(moment());
- return calculateAverageLevel(levels);
- };
+ return (
+ <>
+ {
+ setSelectedAssignment(undefined);
+ setIsCreatingAssignment(false);
+ reloadAssignments();
+ }}
+ assignment={selectedAssignment}
+ />
+ x.admin === user.id || x.participants.includes(user.id))}
+ users={users.filter(
+ (x) =>
+ x.type === "student" &&
+ (!!selectedUser
+ ? groups
+ .filter((g) => g.admin === selectedUser.id)
+ .flatMap((g) => g.participants)
+ .includes(x.id) || false
+ : groups.flatMap((g) => g.participants).includes(x.id)),
+ )}
+ assigner={user.id}
+ isCreating={isCreatingAssignment}
+ cancelCreation={() => {
+ setIsCreatingAssignment(false);
+ setSelectedAssignment(undefined);
+ reloadAssignments();
+ }}
+ />
+
+
setPage("")}
+ className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300">
+
+ Back
+
+
+ Reload
+
+
+
+
+ Active Assignments ({assignments.filter(activeFilter).length})
+
+ {assignments.filter(activeFilter).map((a) => (
+
setSelectedAssignment(a)} key={a.id} />
+ ))}
+
+
+
+ Planned Assignments ({assignments.filter(futureFilter).length})
+
+
setIsCreatingAssignment(true)}
+ className="w-[250px] h-[200px] flex flex-col gap-2 items-center justify-center bg-white hover:bg-mti-purple-ultralight text-mti-purple-light hover:text-mti-purple-dark border border-mti-gray-platinum hover:drop-shadow p-4 cursor-pointer rounded-xl transition ease-in-out duration-300">
+
+ New Assignment
+
+ {assignments.filter(futureFilter).map((a) => (
+
{
+ setSelectedAssignment(a);
+ setIsCreatingAssignment(true);
+ }}
+ key={a.id}
+ />
+ ))}
+
+
+
+ Past Assignments ({assignments.filter(pastFilter).length})
+
+ {assignments.filter(pastFilter).map((a) => (
+
setSelectedAssignment(a)}
+ key={a.id}
+ allowDownload
+ reload={reloadAssignments}
+ allowArchive
+ />
+ ))}
+
+
+
+ Archived Assignments ({assignments.filter(archivedFilter).length})
+
+ {assignments.filter(archivedFilter).map((a) => (
+
setSelectedAssignment(a)}
+ key={a.id}
+ allowDownload
+ reload={reloadAssignments}
+ allowUnarchive
+ />
+ ))}
+
+
+ >
+ );
+ };
- const DefaultDashboard = () => (
- <>
-
- setPage("students")}
- Icon={BsPersonFill}
- label="Students"
- value={users.filter(studentFilter).length}
- color="purple"
- />
- setPage("teachers")}
- Icon={BsPencilSquare}
- label="Teachers"
- value={users.filter(teacherFilter).length}
- color="purple"
- />
-
- groups.flatMap((g) => g.participants).includes(s.user)
- ).length
- }
- color="purple"
- />
-
- groups.flatMap((g) => g.participants).includes(s.user)
- )
- ).toFixed(1)}
- color="purple"
- />
- setPage("groups")}
- Icon={BsPeople}
- label="Groups"
- value={groups.length}
- color="purple"
- />
-
-
- setPage("corporate")}
- />
-
+ const averageLevelCalculator = (studentStats: Stat[]) => {
+ const formattedStats = studentStats
+ .map((s) => ({
+ focus: users.find((u) => u.id === s.user)?.focus,
+ score: s.score,
+ module: s.module,
+ }))
+ .filter((f) => !!f.focus);
+ const bandScores = formattedStats.map((s) => ({
+ module: s.module,
+ level: calculateBandScore(s.score.correct, s.score.total, s.module, s.focus!),
+ }));
-
-
-
Latest students
-
- {users
- .filter(studentFilter)
- .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
- .map((x) => (
-
- ))}
-
-
-
-
Latest teachers
-
- {users
- .filter(teacherFilter)
- .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
- .map((x) => (
-
- ))}
-
-
-
-
Highest level students
-
- {users
- .filter(studentFilter)
- .sort(
- (a, b) =>
- calculateAverageLevel(b.levels) -
- calculateAverageLevel(a.levels)
- )
- .map((x) => (
-
- ))}
-
-
-
-
Highest exam count students
-
- {users
- .filter(studentFilter)
- .sort(
- (a, b) =>
- Object.keys(groupByExam(getStatsByStudent(b))).length -
- Object.keys(groupByExam(getStatsByStudent(a))).length
- )
- .map((x) => (
-
- ))}
-
-
-
- >
- );
+ const levels: {[key in Module]: number} = {
+ reading: 0,
+ listening: 0,
+ writing: 0,
+ speaking: 0,
+ level: 0,
+ };
+ bandScores.forEach((b) => (levels[b.module] += b.level));
- return (
- <>
- setSelectedUser(undefined)}>
- <>
- {selectedUser && (
-
- {
- setSelectedUser(undefined);
- if (shouldReload) reload();
- }}
- onViewStudents={
- selectedUser.type === "corporate" ||
- selectedUser.type === "teacher"
- ? () => {
- appendUserFilters({
- id: "view-students",
- filter: (x: User) => x.type === "student",
- });
- appendUserFilters({
- id: "belongs-to-admin",
- filter: (x: User) =>
- groups
- .filter(
- (g) =>
- g.admin === selectedUser.id ||
- g.participants.includes(selectedUser.id)
- )
- .flatMap((g) => g.participants)
- .includes(x.id),
- });
+ return calculateAverageLevel(levels);
+ };
- router.push("/list/users");
- }
- : undefined
- }
- onViewTeachers={
- selectedUser.type === "corporate" ||
- selectedUser.type === "student"
- ? () => {
- appendUserFilters({
- id: "view-teachers",
- filter: (x: User) => x.type === "teacher",
- });
- appendUserFilters({
- id: "belongs-to-admin",
- filter: (x: User) =>
- groups
- .filter(
- (g) =>
- g.admin === selectedUser.id ||
- g.participants.includes(selectedUser.id)
- )
- .flatMap((g) => g.participants)
- .includes(x.id),
- });
+ const DefaultDashboard = () => (
+ <>
+
+ setPage("students")}
+ Icon={BsPersonFill}
+ label="Students"
+ value={users.filter(studentFilter).length}
+ color="purple"
+ />
+ setPage("teachers")}
+ Icon={BsPencilSquare}
+ label="Teachers"
+ value={users.filter(teacherFilter).length}
+ color="purple"
+ />
+ groups.flatMap((g) => g.participants).includes(s.user)).length}
+ color="purple"
+ />
+ groups.flatMap((g) => g.participants).includes(s.user))).toFixed(1)}
+ color="purple"
+ />
+ setPage("groups")} Icon={BsPeople} label="Groups" value={groups.length} color="purple" />
+
+
+ setPage("corporate")}
+ />
+
+
- router.push("/list/users");
- }
- : undefined
- }
- user={selectedUser}
- />
-
- )}
- >
-
- {page === "students" && }
- {page === "teachers" && }
- {page === "groups" && }
- {page === "corporate" && }
- {page === "" && }
- >
- );
+
+
+
Latest students
+
+ {users
+ .filter(studentFilter)
+ .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
+ .map((x) => (
+
+ ))}
+
+
+
+
Latest teachers
+
+ {users
+ .filter(teacherFilter)
+ .sort((a, b) => dateSorter(a, b, "desc", "registrationDate"))
+ .map((x) => (
+
+ ))}
+
+
+
+
Highest level students
+
+ {users
+ .filter(studentFilter)
+ .sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))
+ .map((x) => (
+
+ ))}
+
+
+
+
Highest exam count students
+
+ {users
+ .filter(studentFilter)
+ .sort(
+ (a, b) =>
+ Object.keys(groupByExam(getStatsByStudent(b))).length - Object.keys(groupByExam(getStatsByStudent(a))).length,
+ )
+ .map((x) => (
+
+ ))}
+
+
+
+ >
+ );
+
+ return (
+ <>
+ setSelectedUser(undefined)}>
+ <>
+ {selectedUser && (
+
+ {
+ setSelectedUser(undefined);
+ if (shouldReload) reload();
+ }}
+ onViewStudents={
+ selectedUser.type === "corporate" || selectedUser.type === "teacher"
+ ? () => {
+ appendUserFilters({
+ id: "view-students",
+ filter: (x: User) => x.type === "student",
+ });
+ appendUserFilters({
+ id: "belongs-to-admin",
+ filter: (x: User) =>
+ groups
+ .filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id))
+ .flatMap((g) => g.participants)
+ .includes(x.id),
+ });
+
+ router.push("/list/users");
+ }
+ : undefined
+ }
+ onViewTeachers={
+ selectedUser.type === "corporate" || selectedUser.type === "student"
+ ? () => {
+ appendUserFilters({
+ id: "view-teachers",
+ filter: (x: User) => x.type === "teacher",
+ });
+ appendUserFilters({
+ id: "belongs-to-admin",
+ filter: (x: User) =>
+ groups
+ .filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id))
+ .flatMap((g) => g.participants)
+ .includes(x.id),
+ });
+
+ router.push("/list/users");
+ }
+ : undefined
+ }
+ user={selectedUser}
+ />
+
+ )}
+ >
+
+ {page === "students" && }
+ {page === "teachers" && }
+ {page === "groups" && }
+ {page === "corporate" && }
+ {page === "assignments" && }
+ {page === "" && }
+ >
+ );
}
diff --git a/src/hooks/useAssignments.tsx b/src/hooks/useAssignments.tsx
index 8e694904..2d2f58ef 100644
--- a/src/hooks/useAssignments.tsx
+++ b/src/hooks/useAssignments.tsx
@@ -2,7 +2,7 @@ import {Assignment} from "@/interfaces/results";
import axios from "axios";
import {useEffect, useState} from "react";
-export default function useAssignments({assigner, assignees}: {assigner?: string; assignees?: string}) {
+export default function useAssignments({assigner, assignees, corporate}: {assigner?: string; assignees?: string; corporate?: string}) {
const [assignments, setAssignments] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
@@ -10,12 +10,13 @@ export default function useAssignments({assigner, assignees}: {assigner?: string
const getData = () => {
setIsLoading(true);
axios
- .get("/api/assignments")
- .then((response) => {
+ .get(!corporate ? "/api/assignments" : `/api/assignments/corporate?id=${corporate}`)
+ .then(async (response) => {
if (assigner) {
setAssignments(response.data.filter((a) => a.assigner === assigner));
return;
}
+
if (assignees) {
setAssignments(response.data.filter((a) => a.assignees.filter((x) => assignees.includes(x)).length > 0));
return;
@@ -26,7 +27,7 @@ export default function useAssignments({assigner, assignees}: {assigner?: string
.finally(() => setIsLoading(false));
};
- useEffect(getData, [assignees, assigner]);
+ useEffect(getData, [assignees, assigner, corporate]);
return {assignments, isLoading, isError, reload: getData};
}
diff --git a/src/pages/api/assignments/corporate.ts b/src/pages/api/assignments/corporate.ts
new file mode 100644
index 00000000..21d1767f
--- /dev/null
+++ b/src/pages/api/assignments/corporate.ts
@@ -0,0 +1,40 @@
+// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
+import type {NextApiRequest, NextApiResponse} from "next";
+import {app} from "@/firebase";
+import {getFirestore, collection, getDocs, query, where, setDoc, doc, getDoc} from "firebase/firestore";
+import {withIronSessionApiRoute} from "iron-session/next";
+import {sessionOptions} from "@/lib/session";
+import {uuidv4} from "@firebase/util";
+import {Module} from "@/interfaces";
+import {getExams} from "@/utils/exams.be";
+import {Exam, InstructorGender, Variant} from "@/interfaces/exam";
+import {capitalize, flatten, uniqBy} from "lodash";
+import {User} from "@/interfaces/user";
+import moment from "moment";
+import {sendEmail} from "@/email";
+import {getAllAssignersByCorporate} from "@/utils/groups.be";
+import {getAssignmentsByAssigners} from "@/utils/assignments.be";
+
+const db = getFirestore(app);
+
+export default withIronSessionApiRoute(handler, sessionOptions);
+
+async function handler(req: NextApiRequest, res: NextApiResponse) {
+ if (!req.session.user) {
+ res.status(401).json({ok: false});
+ return;
+ }
+
+ if (req.method === "GET") return GET(req, res);
+
+ res.status(404).json({ok: false});
+}
+
+async function GET(req: NextApiRequest, res: NextApiResponse) {
+ const {id} = req.query as {id: string};
+
+ const assigners = await getAllAssignersByCorporate(id);
+ const assignments = await getAssignmentsByAssigners([...assigners, id]);
+
+ res.status(200).json(assignments);
+}
diff --git a/src/utils/assignments.be.ts b/src/utils/assignments.be.ts
new file mode 100644
index 00000000..96725e67
--- /dev/null
+++ b/src/utils/assignments.be.ts
@@ -0,0 +1,14 @@
+import {app} from "@/firebase";
+import {Assignment} from "@/interfaces/results";
+import {collection, getDocs, getFirestore, query, where} from "firebase/firestore";
+
+const db = getFirestore(app);
+
+export const getAssignmentsByAssigner = async (id: string) => {
+ const {docs} = await getDocs(query(collection(db, "assignments"), where("assigner", "==", id)));
+ return docs.map((x) => ({...x.data(), id: x.id})) as Assignment[];
+};
+
+export const getAssignmentsByAssigners = async (ids: string[]) => {
+ return (await Promise.all(ids.map(getAssignmentsByAssigner))).flat();
+};
diff --git a/src/utils/groups.be.ts b/src/utils/groups.be.ts
index 8dae61c8..77fc5d23 100644
--- a/src/utils/groups.be.ts
+++ b/src/utils/groups.be.ts
@@ -1,53 +1,51 @@
-import { app } from "@/firebase";
-import { CorporateUser, StudentUser, TeacherUser } from "@/interfaces/user";
-import { doc, getDoc, getFirestore, setDoc } from "firebase/firestore";
+import {app} from "@/firebase";
+import {CorporateUser, Group, StudentUser, TeacherUser} from "@/interfaces/user";
+import {collection, doc, getDoc, getDocs, getFirestore, query, setDoc, where} from "firebase/firestore";
import moment from "moment";
+import {getUser} from "./users.be";
const db = getFirestore(app);
-export const updateExpiryDateOnGroup = async (
- participantID: string,
- corporateID: string,
-) => {
- const corporateRef = await getDoc(doc(db, "users", corporateID));
- const participantRef = await getDoc(doc(db, "users", participantID));
+export const updateExpiryDateOnGroup = async (participantID: string, corporateID: string) => {
+ const corporateRef = await getDoc(doc(db, "users", corporateID));
+ const participantRef = await getDoc(doc(db, "users", participantID));
- if (!corporateRef.exists() || !participantRef.exists()) return;
+ if (!corporateRef.exists() || !participantRef.exists()) return;
- const corporate = {
- ...corporateRef.data(),
- id: corporateRef.id,
- } as CorporateUser;
- const participant = { ...participantRef.data(), id: participantRef.id } as
- | StudentUser
- | TeacherUser;
+ const corporate = {
+ ...corporateRef.data(),
+ id: corporateRef.id,
+ } as CorporateUser;
+ const participant = {...participantRef.data(), id: participantRef.id} as StudentUser | TeacherUser;
- if (
- corporate.type !== "corporate" ||
- (participant.type !== "student" && participant.type !== "teacher")
- )
- return;
+ if (corporate.type !== "corporate" || (participant.type !== "student" && participant.type !== "teacher")) return;
- if (
- !corporate.subscriptionExpirationDate ||
- !participant.subscriptionExpirationDate
- ) {
- return await setDoc(
- doc(db, "users", participant.id),
- { subscriptionExpirationDate: null },
- { merge: true },
- );
- }
+ if (!corporate.subscriptionExpirationDate || !participant.subscriptionExpirationDate) {
+ return await setDoc(doc(db, "users", participant.id), {subscriptionExpirationDate: null}, {merge: true});
+ }
- const corporateDate = moment(corporate.subscriptionExpirationDate);
- const participantDate = moment(participant.subscriptionExpirationDate);
+ const corporateDate = moment(corporate.subscriptionExpirationDate);
+ const participantDate = moment(participant.subscriptionExpirationDate);
- if (corporateDate.isAfter(participantDate))
- return await setDoc(
- doc(db, "users", participant.id),
- { subscriptionExpirationDate: corporateDate.toISOString() },
- { merge: true },
- );
+ if (corporateDate.isAfter(participantDate))
+ return await setDoc(doc(db, "users", participant.id), {subscriptionExpirationDate: corporateDate.toISOString()}, {merge: true});
- return;
+ return;
+};
+
+export const getUserGroups = async (id: string): Promise => {
+ const groupDocs = await getDocs(query(collection(db, "groups"), where("admin", "==", id)));
+ return groupDocs.docs.map((x) => ({...x.data(), id})) as Group[];
+};
+
+export const getAllAssignersByCorporate = async (corporateID: string): Promise => {
+ const groups = await getUserGroups(corporateID);
+ const groupUsers = (await Promise.all(groups.map(async (g) => await Promise.all(g.participants.map(getUser))))).flat();
+ const teacherPromises = await Promise.all(
+ groupUsers.map(async (u) =>
+ u.type === "teacher" ? u.id : u.type === "corporate" ? [...(await getAllAssignersByCorporate(u.id)), u.id] : undefined,
+ ),
+ );
+
+ return teacherPromises.filter((x) => !!x).flat() as string[];
};
diff --git a/src/utils/users.be.ts b/src/utils/users.be.ts
index 7650f73c..aef0634e 100644
--- a/src/utils/users.be.ts
+++ b/src/utils/users.be.ts
@@ -1,14 +1,20 @@
-import { app } from "@/firebase";
+import {app} from "@/firebase";
-import { collection, getDocs, getFirestore } from "firebase/firestore";
-import { User } from "@/interfaces/user";
+import {collection, doc, getDoc, getDocs, getFirestore} from "firebase/firestore";
+import {User} from "@/interfaces/user";
const db = getFirestore(app);
export async function getUsers() {
- const snapshot = await getDocs(collection(db, "users"));
+ const snapshot = await getDocs(collection(db, "users"));
- return snapshot.docs.map((doc) => ({
- id: doc.id,
- ...doc.data(),
- })) as User[];
+ return snapshot.docs.map((doc) => ({
+ id: doc.id,
+ ...doc.data(),
+ })) as User[];
+}
+
+export async function getUser(id: string) {
+ const userDoc = await getDoc(doc(db, "users", id));
+
+ return {...userDoc.data(), id} as User;
}
diff --git a/src/utils/users.ts b/src/utils/users.ts
index a3a500fd..619d9ea9 100644
--- a/src/utils/users.ts
+++ b/src/utils/users.ts
@@ -25,7 +25,10 @@ export const exportListToExcel = (rowUsers: User[], users: User[], groups: Group
expiryDate: user.subscriptionExpirationDate ? moment(user.subscriptionExpirationDate).format("DD/MM/YYYY") : "Unlimited",
country: user.demographicInformation?.country || "N/A",
phone: user.demographicInformation?.phone || "N/A",
- employmentPosition: (user.type === "corporate" || user.type === "mastercorporate" ? user.demographicInformation?.position : user.demographicInformation?.employment) || "N/A",
+ employmentPosition:
+ (user.type === "corporate" || user.type === "mastercorporate"
+ ? user.demographicInformation?.position
+ : user.demographicInformation?.employment) || "N/A",
gender: user.demographicInformation?.gender ? capitalize(user.demographicInformation.gender) : "N/A",
verified: user.isVerified?.toString() || "FALSE",
}));
@@ -34,3 +37,9 @@ export const exportListToExcel = (rowUsers: User[], users: User[], groups: Group
return `${header}\n${rowsString}`;
};
+
+export const getUserName = (user?: User) => {
+ if (!user) return "N/A";
+ if (user.type === "corporate" || user.type === "mastercorporate") return user.corporateInformation?.companyInformation?.name || user.name;
+ return user.name;
+};