From 27841178622f6022ca41d3c33b5ca670c09905f1 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Tue, 13 Aug 2024 10:02:40 +0100 Subject: [PATCH] Updated the MasterCorporate and Corporate pages to allow to have Assignments --- src/dashboards/AssignmentCard.tsx | 16 +- src/dashboards/AssignmentView.tsx | 18 +- src/dashboards/Corporate.tsx | 824 +++++++++++++----------- src/dashboards/MasterCorporate.tsx | 835 ++++++++++++++----------- src/hooks/useAssignments.tsx | 9 +- src/pages/api/assignments/corporate.ts | 40 ++ src/utils/assignments.be.ts | 14 + src/utils/groups.be.ts | 80 ++- src/utils/users.be.ts | 22 +- src/utils/users.ts | 11 +- 10 files changed, 1050 insertions(+), 819 deletions(-) create mode 100644 src/pages/api/assignments/corporate.ts create mode 100644 src/utils/assignments.be.ts 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.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.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.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.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; +};