diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 02f82022..6c1320e5 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,118 +1,219 @@ -import {User} from "@/interfaces/user"; +import { User } from "@/interfaces/user"; import Link from "next/link"; import FocusLayer from "@/components/FocusLayer"; -import {preventNavigation} from "@/utils/navigation.disabled"; -import {useRouter} from "next/router"; -import {BsList, BsQuestionCircle, BsQuestionCircleFill} from "react-icons/bs"; +import { preventNavigation } from "@/utils/navigation.disabled"; +import { useRouter } from "next/router"; +import { BsList, BsQuestionCircle, BsQuestionCircleFill } from "react-icons/bs"; import clsx from "clsx"; import moment from "moment"; import MobileMenu from "./MobileMenu"; -import {useEffect, useState} from "react"; -import {Type} from "@/interfaces/user"; -import {USER_TYPE_LABELS} from "@/resources/user"; +import { useEffect, useState } from "react"; +import { Type } from "@/interfaces/user"; +import { USER_TYPE_LABELS } from "@/resources/user"; import useGroups from "@/hooks/useGroups"; -import {isUserFromCorporate} from "@/utils/groups"; +import { isUserFromCorporate } from "@/utils/groups"; import Button from "./Low/Button"; import Modal from "./Modal"; import Input from "./Low/Input"; import TicketSubmission from "./High/TicketSubmission"; +import { Module } from "@/interfaces"; +import Badge from "./Low/Badge"; +import { + BsArrowRepeat, + BsBook, + BsCheck, + BsCheckCircle, + BsClipboard, + BsHeadphones, + BsMegaphone, + BsPen, + BsXCircle, +} from "react-icons/bs"; interface Props { - user: User; - navDisabled?: boolean; - focusMode?: boolean; - onFocusLayerMouseEnter?: () => void; - path: string; + user: User; + navDisabled?: boolean; + focusMode?: boolean; + onFocusLayerMouseEnter?: () => void; + path: string; } /* eslint-disable @next/next/no-img-element */ -export default function Navbar({user, path, navDisabled = false, focusMode = false, onFocusLayerMouseEnter}: Props) { - const [isMenuOpen, setIsMenuOpen] = useState(false); - const [disablePaymentPage, setDisablePaymentPage] = useState(true); - const [isTicketOpen, setIsTicketOpen] = useState(false); +export default function Navbar({ + user, + path, + navDisabled = false, + focusMode = false, + onFocusLayerMouseEnter, +}: Props) { + const [isMenuOpen, setIsMenuOpen] = useState(false); + const [disablePaymentPage, setDisablePaymentPage] = useState(true); + const [isTicketOpen, setIsTicketOpen] = useState(false); - const router = useRouter(); + const router = useRouter(); - const disableNavigation = preventNavigation(navDisabled, focusMode); + const disableNavigation = preventNavigation(navDisabled, focusMode); - const expirationDateColor = (date: Date) => { - const momentDate = moment(date); - const today = moment(new Date()); + const expirationDateColor = (date: Date) => { + const momentDate = moment(date); + const today = moment(new Date()); - if (today.add(1, "days").isAfter(momentDate)) return "!bg-mti-red-ultralight border-mti-red-light"; - if (today.add(3, "days").isAfter(momentDate)) return "!bg-mti-rose-ultralight border-mti-rose-light"; - if (today.add(7, "days").isAfter(momentDate)) return "!bg-mti-orange-ultralight border-mti-orange-light"; - }; + if (today.add(1, "days").isAfter(momentDate)) + return "!bg-mti-red-ultralight border-mti-red-light"; + if (today.add(3, "days").isAfter(momentDate)) + return "!bg-mti-rose-ultralight border-mti-rose-light"; + if (today.add(7, "days").isAfter(momentDate)) + return "!bg-mti-orange-ultralight border-mti-orange-light"; + }; - const showExpirationDate = () => { - if (!user.subscriptionExpirationDate) return false; + const showExpirationDate = () => { + if (!user.subscriptionExpirationDate) return false; - const momentDate = moment(user.subscriptionExpirationDate); - const today = moment(new Date()); + const momentDate = moment(user.subscriptionExpirationDate); + const today = moment(new Date()); - return today.add(7, "days").isAfter(momentDate); - }; + return today.add(7, "days").isAfter(momentDate); + }; - useEffect(() => { - if (user.type !== "student" && user.type !== "teacher") return setDisablePaymentPage(false); - isUserFromCorporate(user.id).then((result) => setDisablePaymentPage(result)); - }, [user]); + useEffect(() => { + if (user.type !== "student" && user.type !== "teacher") + return setDisablePaymentPage(false); + isUserFromCorporate(user.id).then((result) => + setDisablePaymentPage(result) + ); + }, [user]); - return ( - <> - setIsTicketOpen(false)} title="Submit a ticket"> - setIsTicketOpen(false)} /> - + const badges = [ + { + module: "reading", + icon: () => , + achieved: user.levels.reading >= user.desiredLevels.reading, + }, - {user && ( - setIsMenuOpen(false)} user={user} /> - )} -
- - EnCoach's Logo -

EnCoach

- -
- {/* OPEN TICKET SYSTEM */} - + { + module: "listening", + icon: () => , + achieved: user.levels.listening >= user.desiredLevels.listening, + }, + { + module: "writing", + icon: () => , + achieved: user.levels.writing >= user.desiredLevels.writing, + }, + { + module: "speaking", + icon: () => , + achieved: user.levels.speaking >= user.desiredLevels.speaking, + }, + { + module: "level", + icon: () => , + achieved: user.levels.level >= user.desiredLevels.level, + }, + ]; - {showExpirationDate() && ( - - {!user.subscriptionExpirationDate && "Unlimited"} - {user.subscriptionExpirationDate && moment(user.subscriptionExpirationDate).format("DD/MM/YYYY")} - - )} - - {user.name} - - {user.type === "corporate" ? `${user.corporateInformation?.companyInformation.name} |` : ""} {user.name} |{" "} - {USER_TYPE_LABELS[user.type]} - - -
setIsMenuOpen(true)}> - -
-
- {focusMode && } -
- - ); + return ( + <> + setIsTicketOpen(false)} + title="Submit a ticket" + > + setIsTicketOpen(false)} + /> + + + {user && ( + setIsMenuOpen(false)} + user={user} + /> + )} +
+ + EnCoach's Logo +

EnCoach

+ +
+ {user.type === "student" && + badges.map((badge) => ( +
+ {badge.icon()} +
+ ))} + {/* OPEN TICKET SYSTEM */} + + + {showExpirationDate() && ( + + {!user.subscriptionExpirationDate && "Unlimited"} + {user.subscriptionExpirationDate && + moment(user.subscriptionExpirationDate).format("DD/MM/YYYY")} + + )} + + {user.name} + + {user.type === "corporate" + ? `${user.corporateInformation?.companyInformation.name} |` + : ""}{" "} + {user.name} | {USER_TYPE_LABELS[user.type]} + + +
setIsMenuOpen(true)} + > + +
+
+ {focusMode && ( + + )} +
+ + ); } diff --git a/src/dashboards/Admin.tsx b/src/dashboards/Admin.tsx index 7613a5dd..85939403 100644 --- a/src/dashboards/Admin.tsx +++ b/src/dashboards/Admin.tsx @@ -2,576 +2,714 @@ import Modal from "@/components/Modal"; import useStats from "@/hooks/useStats"; import useUsers from "@/hooks/useUsers"; -import {User} from "@/interfaces/user"; +import { 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, - BsBriefcaseFill, - BsGlobeCentralSouthAsia, - BsPerson, - BsPersonFill, - BsPencilSquare, - BsBank, - BsCurrencyDollar, - BsLayoutWtf, - BsLayoutSidebar, + BsArrowLeft, + BsBriefcaseFill, + BsGlobeCentralSouthAsia, + BsPerson, + BsPersonFill, + BsPencilSquare, + BsBank, + BsCurrencyDollar, + BsLayoutWtf, + BsLayoutSidebar, } from "react-icons/bs"; import UserCard from "@/components/UserCard"; import useGroups from "@/hooks/useGroups"; import IconCard from "./IconCard"; import useFilterStore from "@/stores/listFilterStore"; -import {useRouter} from "next/router"; +import { useRouter } from "next/router"; import usePaymentStatusUsers from "@/hooks/usePaymentStatusUsers"; +import CorporateStudentsLevels from "./CorporateStudentsLevels"; interface Props { - user: User; + user: User; } -export default function AdminDashboard({user}: Props) { - const [page, setPage] = useState(""); - const [selectedUser, setSelectedUser] = useState(); - const [showModal, setShowModal] = useState(false); +export default function AdminDashboard({ user }: Props) { + const [page, setPage] = useState(""); + const [selectedUser, setSelectedUser] = useState(); + const [showModal, setShowModal] = useState(false); - const {stats} = useStats(user.id); - const {users, reload} = useUsers(); - const {groups} = useGroups(); - const {pending, done} = usePaymentStatusUsers(); + const { stats } = useStats(user.id); + const { users, reload } = useUsers(); + const { groups } = useGroups(); + const { pending, done } = usePaymentStatusUsers(); - 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]); - // eslint-disable-next-line react-hooks/exhaustive-deps - useEffect(reload, [page]); + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(reload, [page]); - const inactiveCountryManagerFilter = (x: User) => - x.type === "agent" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)); + const inactiveCountryManagerFilter = (x: User) => + x.type === "agent" && + (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)); - 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.type === "corporate" - ? displayUser.corporateInformation?.companyInformation?.name || 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.type === "corporate" + ? displayUser.corporateInformation?.companyInformation?.name || + displayUser.name + : displayUser.name} + + {displayUser.email} +
+
+ ); - const StudentsList = () => { - const filter = (x: User) => - x.type === "student" && - (!!selectedUser - ? groups - .filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id)) - .flatMap((g) => g.participants) - .includes(x.id) - : true); + const StudentsList = () => { + const filter = (x: User) => + x.type === "student" && + (!!selectedUser + ? groups + .filter( + (g) => + g.admin === selectedUser.id || + g.participants.includes(selectedUser.id) + ) + .flatMap((g) => g.participants) + .includes(x.id) + : true); - 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 ({users.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 +
+

Students ({total})

+
+ )} + /> + ); + }; - - - ); - }; + const TeachersList = () => { + const filter = (x: User) => + x.type === "teacher" && + (!!selectedUser + ? groups + .filter( + (g) => + g.admin === selectedUser.id || + g.participants.includes(selectedUser.id) + ) + .flatMap((g) => g.participants) + .includes(x.id) || false + : true); - const TeachersList = () => { - const filter = (x: User) => - x.type === "teacher" && - (!!selectedUser - ? groups - .filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id)) - .flatMap((g) => g.participants) - .includes(x.id) || false - : true); + 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 ({users.filter(filter).length})

-
+ const AgentsList = () => { + const filter = (x: User) => x.type === "agent"; - - - ); - }; + 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 +
+

+ Country Managers ({total}) +

+
+ )} + /> + ); + }; - const AgentsList = () => { - const filter = (x: User) => x.type === "agent"; + const CorporateList = () => ( + x.type === "corporate"]} + renderHeader={(total) => ( +
+
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 +
+

Corporate ({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 -
-

Country Managers ({users.filter(filter).length})

-
+ const CorporatePaidStatusList = ({ paid }: { paid: Boolean }) => { + const list = paid ? done : pending; + const filter = (x: User) => x.type === "corporate" && list.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 +
+

+ {paid ? "Payment Done" : "Pending Payment"} ({total}) +

+
+ )} + /> + ); + }; - const CorporateList = () => ( - <> -
-
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 -
-

Corporate ({users.filter((x) => x.type === "corporate").length})

-
+ const InactiveCountryManagerList = () => { + 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 +
+

+ Inactive Country Managers ({total}) +

+
+ )} + /> + ); + }; - x.type === "corporate"]} /> - - ); + const InactiveStudentsList = () => { + const filter = (x: User) => + x.type === "student" && + (x.status === "disabled" || + moment().isAfter(x.subscriptionExpirationDate)); - const CorporatePaidStatusList = ({paid}: {paid: Boolean}) => { - const list = paid ? done : pending; - const filter = (x: User) => x.type === "corporate" && list.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 +
+

+ Inactive 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 -
-

- {paid ? "Payment Done" : "Pending Payment"} ({list.length}) -

-
- - - ); - }; + const InactiveCorporateList = () => { + const filter = (x: User) => + x.type === "corporate" && + (x.status === "disabled" || + moment().isAfter(x.subscriptionExpirationDate)); - const InactiveCountryManagerList = () => { - 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 -
-

Inactive Country Managers ({users.filter(inactiveCountryManagerFilter).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 +
+

+ Inactive Corporate ({total}) +

+
+ )} + /> + ); + }; - - - ); - }; + const CorporateStudentsLevelsHelper = () => { + 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 +
+

+ Corporate Students Levels +

+
+ + + ); + }; - const InactiveStudentsList = () => { - const filter = (x: User) => x.type === "student" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)); + const DefaultDashboard = () => ( + <> +
+ x.type === "student").length} + onClick={() => setPage("students")} + color="purple" + /> + x.type === "teacher").length} + onClick={() => setPage("teachers")} + color="purple" + /> + x.type === "corporate").length} + onClick={() => setPage("corporate")} + color="purple" + /> + x.type === "agent").length} + onClick={() => setPage("agents")} + color="purple" + /> + x.demographicInformation) + .map((x) => x.demographicInformation?.country) + ), + ].length + } + color="purple" + /> + setPage("inactiveStudents")} + Icon={BsPersonFill} + label="Inactive Students" + value={ + users.filter( + (x) => + x.type === "student" && + (x.status === "disabled" || + moment().isAfter(x.subscriptionExpirationDate)) + ).length + } + color="rose" + /> + setPage("inactiveCountryManagers")} + Icon={BsBriefcaseFill} + label="Inactive Country Managers" + value={users.filter(inactiveCountryManagerFilter).length} + color="rose" + /> + setPage("inactiveCorporate")} + Icon={BsBank} + label="Inactive Corporate" + value={ + users.filter( + (x) => + x.type === "corporate" && + (x.status === "disabled" || + moment().isAfter(x.subscriptionExpirationDate)) + ).length + } + color="rose" + /> + setPage("paymentdone")} + Icon={BsCurrencyDollar} + label="Payment Done" + value={done.length} + color="purple" + /> + setPage("paymentpending")} + Icon={BsCurrencyDollar} + label="Pending Payment" + value={pending.length} + color="rose" + /> + router.push("https://cms.encoach.com/admin")} + Icon={BsLayoutSidebar} + label="Content Management System (CMS)" + color="green" + /> + setPage("corporatestudentslevels")} + Icon={BsPersonFill} + label="Corporate Students Levels" + color="purple" + /> +
- 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 -
-

Inactive Students ({users.filter(filter).length})

-
+
+
+ Latest students +
+ {users + .filter((x) => x.type === "student") + .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) + .map((x) => ( + + ))} +
+
+
+ Latest teachers +
+ {users + .filter((x) => x.type === "teacher") + .sort((a, b) => { + return dateSorter(a, b, "desc", "registrationDate"); + }) + .map((x) => ( + + ))} +
+
+
+ Latest corporate +
+ {users + .filter((x) => x.type === "corporate") + .sort((a, b) => { + return dateSorter(a, b, "desc", "registrationDate"); + }) + .map((x) => ( + + ))} +
+
+
+ Unpaid Corporate +
+ {users + .filter( + (x) => x.type === "corporate" && x.status === "paymentDue" + ) + .map((x) => ( + + ))} +
+
+
+ Students expiring in 1 month +
+ {users + .filter( + (x) => + x.type === "student" && + x.subscriptionExpirationDate && + moment().isAfter( + moment(x.subscriptionExpirationDate).subtract(30, "days") + ) && + moment().isBefore(moment(x.subscriptionExpirationDate)) + ) + .map((x) => ( + + ))} +
+
+
+ Teachers expiring in 1 month +
+ {users + .filter( + (x) => + x.type === "teacher" && + x.subscriptionExpirationDate && + moment().isAfter( + moment(x.subscriptionExpirationDate).subtract(30, "days") + ) && + moment().isBefore(moment(x.subscriptionExpirationDate)) + ) + .map((x) => ( + + ))} +
+
+
+ Country Manager expiring in 1 month +
+ {users + .filter( + (x) => + x.type === "agent" && + x.subscriptionExpirationDate && + moment().isAfter( + moment(x.subscriptionExpirationDate).subtract(30, "days") + ) && + moment().isBefore(moment(x.subscriptionExpirationDate)) + ) + .map((x) => ( + + ))} +
+
+
+ Corporate expiring in 1 month +
+ {users + .filter( + (x) => + x.type === "corporate" && + x.subscriptionExpirationDate && + moment().isAfter( + moment(x.subscriptionExpirationDate).subtract(30, "days") + ) && + moment().isBefore(moment(x.subscriptionExpirationDate)) + ) + .map((x) => ( + + ))} +
+
+
+ Expired Students +
+ {users + .filter( + (x) => + x.type === "student" && + x.subscriptionExpirationDate && + moment().isAfter(moment(x.subscriptionExpirationDate)) + ) + .map((x) => ( + + ))} +
+
+
+ Expired Teachers +
+ {users + .filter( + (x) => + x.type === "teacher" && + x.subscriptionExpirationDate && + moment().isAfter(moment(x.subscriptionExpirationDate)) + ) + .map((x) => ( + + ))} +
+
+
+ Expired Country Manager +
+ {users + .filter( + (x) => + x.type === "agent" && + x.subscriptionExpirationDate && + moment().isAfter(moment(x.subscriptionExpirationDate)) + ) + .map((x) => ( + + ))} +
+
+
+ Expired Corporate +
+ {users + .filter( + (x) => + x.type === "corporate" && + x.subscriptionExpirationDate && + moment().isAfter(moment(x.subscriptionExpirationDate)) + ) + .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), + }); - const InactiveCorporateList = () => { - const filter = (x: User) => x.type === "corporate" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)); + 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), + }); - 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 -
-

Inactive Corporate ({users.filter(filter).length})

-
+ router.push("/list/users"); + } + : undefined + } + onViewCorporate={ + selectedUser.type === "teacher" || + selectedUser.type === "student" + ? () => { + appendUserFilters({ + id: "view-corporate", + filter: (x: User) => x.type === "corporate", + }); + appendUserFilters({ + id: "belongs-to-admin", + filter: (x: User) => + groups + .filter((g) => + g.participants.includes(selectedUser.id) + ) + .flatMap((g) => [g.admin, ...g.participants]) + .includes(x.id), + }); - - - ); - }; - - const DefaultDashboard = () => ( - <> -
- x.type === "student").length} - onClick={() => setPage("students")} - color="purple" - /> - x.type === "teacher").length} - onClick={() => setPage("teachers")} - color="purple" - /> - x.type === "corporate").length} - onClick={() => setPage("corporate")} - color="purple" - /> - x.type === "agent").length} - onClick={() => setPage("agents")} - color="purple" - /> - x.demographicInformation).map((x) => x.demographicInformation?.country))].length} - color="purple" - /> - setPage("inactiveStudents")} - Icon={BsPersonFill} - label="Inactive Students" - value={ - users.filter((x) => x.type === "student" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate))) - .length - } - color="rose" - /> - setPage("inactiveCountryManagers")} - Icon={BsBriefcaseFill} - label="Inactive Country Managers" - value={users.filter(inactiveCountryManagerFilter).length} - color="rose" - /> - setPage("inactiveCorporate")} - Icon={BsBank} - label="Inactive Corporate" - value={ - users.filter((x) => x.type === "corporate" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate))) - .length - } - color="rose" - /> - setPage("paymentdone")} Icon={BsCurrencyDollar} label="Payment Done" value={done.length} color="purple" /> - setPage("paymentpending")} - Icon={BsCurrencyDollar} - label="Pending Payment" - value={pending.length} - color="rose" - /> - router.push("https://cms.encoach.com/admin")} - Icon={BsLayoutSidebar} - label="Content Management System (CMS)" - color="green" - /> -
- -
-
- Latest students -
- {users - .filter((x) => x.type === "student") - .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) - .map((x) => ( - - ))} -
-
-
- Latest teachers -
- {users - .filter((x) => x.type === "teacher") - .sort((a, b) => { - return dateSorter(a, b, "desc", "registrationDate"); - }) - .map((x) => ( - - ))} -
-
-
- Latest corporate -
- {users - .filter((x) => x.type === "corporate") - .sort((a, b) => { - return dateSorter(a, b, "desc", "registrationDate"); - }) - .map((x) => ( - - ))} -
-
-
- Unpaid Corporate -
- {users - .filter((x) => x.type === "corporate" && x.status === "paymentDue") - .map((x) => ( - - ))} -
-
-
- Students expiring in 1 month -
- {users - .filter( - (x) => - x.type === "student" && - x.subscriptionExpirationDate && - moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) && - moment().isBefore(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Teachers expiring in 1 month -
- {users - .filter( - (x) => - x.type === "teacher" && - x.subscriptionExpirationDate && - moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) && - moment().isBefore(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Country Manager expiring in 1 month -
- {users - .filter( - (x) => - x.type === "agent" && - x.subscriptionExpirationDate && - moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) && - moment().isBefore(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Corporate expiring in 1 month -
- {users - .filter( - (x) => - x.type === "corporate" && - x.subscriptionExpirationDate && - moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) && - moment().isBefore(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Expired Students -
- {users - .filter( - (x) => x.type === "student" && x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Expired Teachers -
- {users - .filter( - (x) => x.type === "teacher" && x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Expired Country Manager -
- {users - .filter( - (x) => x.type === "agent" && x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Expired Corporate -
- {users - .filter( - (x) => - x.type === "corporate" && x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate)), - ) - .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 - } - onViewCorporate={ - selectedUser.type === "teacher" || selectedUser.type === "student" - ? () => { - appendUserFilters({ - id: "view-corporate", - filter: (x: User) => x.type === "corporate", - }); - appendUserFilters({ - id: "belongs-to-admin", - filter: (x: User) => - groups - .filter((g) => g.participants.includes(selectedUser.id)) - .flatMap((g) => [g.admin, ...g.participants]) - .includes(x.id), - }); - - router.push("/list/users"); - } - : undefined - } - user={selectedUser} - /> -
- )} - -
- {page === "students" && } - {page === "teachers" && } - {page === "corporate" && } - {page === "agents" && } - {page === "inactiveStudents" && } - {page === "inactiveCorporate" && } - {page === "inactiveCountryManagers" && } - {page === "paymentdone" && } - {page === "paymentpending" && } - {page === "" && } - - ); + router.push("/list/users"); + } + : undefined + } + user={selectedUser} + /> +
+ )} + +
+ {page === "students" && } + {page === "teachers" && } + {page === "corporate" && } + {page === "agents" && } + {page === "inactiveStudents" && } + {page === "inactiveCorporate" && } + {page === "inactiveCountryManagers" && } + {page === "paymentdone" && } + {page === "paymentpending" && } + {page === "corporatestudentslevels" && } + {page === "" && } + + ); } diff --git a/src/dashboards/Agent.tsx b/src/dashboards/Agent.tsx index 88148158..bff2c46d 100644 --- a/src/dashboards/Agent.tsx +++ b/src/dashboards/Agent.tsx @@ -2,243 +2,294 @@ import Modal from "@/components/Modal"; import useStats from "@/hooks/useStats"; import useUsers from "@/hooks/useUsers"; -import { User} from "@/interfaces/user"; +import { 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 {BsArrowLeft, BsPersonFill, BsBank, BsCurrencyDollar} from "react-icons/bs"; +import { useEffect, useState } from "react"; +import { + BsArrowLeft, + BsPersonFill, + BsBank, + BsCurrencyDollar, +} from "react-icons/bs"; import UserCard from "@/components/UserCard"; import useGroups from "@/hooks/useGroups"; import IconCard from "./IconCard"; -import usePaymentStatusUsers from '@/hooks/usePaymentStatusUsers'; +import usePaymentStatusUsers from "@/hooks/usePaymentStatusUsers"; interface Props { - user: User; + user: User; } -export default function AgentDashboard({user}: Props) { - const [page, setPage] = useState(""); - const [selectedUser, setSelectedUser] = useState(); - const [showModal, setShowModal] = useState(false); +export default function AgentDashboard({ user }: Props) { + const [page, setPage] = useState(""); + const [selectedUser, setSelectedUser] = useState(); + const [showModal, setShowModal] = useState(false); - const {stats} = useStats(); - const {users, reload} = useUsers(); - const {groups} = useGroups(user.id); - const { pending, done } = usePaymentStatusUsers(); + const { stats } = useStats(); + const { users, reload } = useUsers(); + const { groups } = useGroups(user.id); + const { pending, done } = usePaymentStatusUsers(); - useEffect(() => { - setShowModal(!!selectedUser && page === ""); - }, [selectedUser, page]); + useEffect(() => { + setShowModal(!!selectedUser && page === ""); + }, [selectedUser, page]); - const corporateFilter = (user: User) => user.type === "corporate"; - const referredCorporateFilter = (x: User) => - x.type === "corporate" && !!x.corporateInformation && x.corporateInformation.referralAgent === user.id; - const inactiveReferredCorporateFilter = (x: User) => - referredCorporateFilter(x) && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)); + const corporateFilter = (user: User) => user.type === "corporate"; + const referredCorporateFilter = (x: User) => + x.type === "corporate" && + !!x.corporateInformation && + x.corporateInformation.referralAgent === user.id; + const inactiveReferredCorporateFilter = (x: User) => + referredCorporateFilter(x) && + (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)); - const UserDisplay = ({ displayUser, allowClick = true }: {displayUser: User, allowClick?: boolean}) => ( -
allowClick && 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.type === "corporate" - ? displayUser.corporateInformation?.companyInformation?.name || displayUser.name - : displayUser.name} - - {displayUser.email} -
-
- ); + const UserDisplay = ({ + displayUser, + allowClick = true, + }: { + displayUser: User; + allowClick?: boolean; + }) => ( +
allowClick && 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.type === "corporate" + ? displayUser.corporateInformation?.companyInformation?.name || + displayUser.name + : displayUser.name} + + {displayUser.email} +
+
+ ); - const ReferredCorporateList = () => { - 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 -
-

Referred Corporate ({users.filter(referredCorporateFilter).length})

-
+ const ReferredCorporateList = () => { + 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 +
+

+ Referred Corporate ({total}) +

+
+ )} + /> + ); + }; - - - ); - }; + const InactiveReferredCorporateList = () => { + 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 +
+

+ Inactive Referred Corporate ({total}) +

+
+ )} + /> + ); + }; - const InactiveReferredCorporateList = () => { - 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 -
-

Inactive Referred Corporate ({users.filter(inactiveReferredCorporateFilter).length})

-
+ const CorporateList = () => { + const filter = (x: User) => x.type === "corporate"; - - - ); - }; + 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 +
+

Corporate ({total})

+
+ )} + /> + ); + }; - const CorporateList = () => { - const filter = (x: User) => x.type === "corporate"; + const CorporatePaidStatusList = ({ paid }: { paid: Boolean }) => { + const list = paid ? done : pending; + const filter = (x: User) => x.type === "corporate" && list.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 -
-

Corporate ({users.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 +
+

+ {paid ? "Payment Done" : "Pending Payment"} ({total}) +

+
+ )} + /> + ); + }; - const CorporatePaidStatusList = ({ paid }: {paid: Boolean}) => { - const list = paid ? done : pending; - const filter = (x: User) => x.type === "corporate" && list.includes(x.id); + const DefaultDashboard = () => ( + <> +
+ setPage("referredCorporate")} + Icon={BsBank} + label="Referred Corporate" + value={users.filter(referredCorporateFilter).length} + color="purple" + /> + setPage("inactiveReferredCorporate")} + Icon={BsBank} + label="Inactive Referred Corporate" + value={users.filter(inactiveReferredCorporateFilter).length} + color="rose" + /> + setPage("corporate")} + Icon={BsBank} + label="Corporate" + value={users.filter(corporateFilter).length} + color="purple" + /> + setPage("paymentdone")} + Icon={BsCurrencyDollar} + label="Payment Done" + value={done.length} + color="purple" + /> + setPage("paymentpending")} + Icon={BsCurrencyDollar} + label="Pending Payment" + value={pending.length} + color="rose" + /> +
- 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 -
-

{paid ? 'Payment Done' : 'Pending Payment'} ({list.length})

-
- - - ); - }; +
+
+ Latest Referred Corporate +
+ {users + .filter(referredCorporateFilter) + .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) + .map((x) => ( + + ))} +
+
+
+ Latest corporate +
+ {users + .filter(corporateFilter) + .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) + .map((x) => ( + + ))} +
+
+
+ Referenced corporate expiring in 1 month +
+ {users + .filter( + (x) => + referredCorporateFilter(x) && + moment().isAfter( + moment(x.subscriptionExpirationDate).subtract(30, "days") + ) && + moment().isBefore(moment(x.subscriptionExpirationDate)) + ) + .map((x) => ( + + ))} +
+
+
+ + ); - const DefaultDashboard = () => ( - <> -
- setPage("referredCorporate")} - Icon={BsBank} - label="Referred Corporate" - value={users.filter(referredCorporateFilter).length} - color="purple" - /> - setPage("inactiveReferredCorporate")} - Icon={BsBank} - label="Inactive Referred Corporate" - value={users.filter(inactiveReferredCorporateFilter).length} - color="rose" - /> - setPage("corporate")} - Icon={BsBank} - label="Corporate" - value={users.filter(corporateFilter).length} - color="purple" - /> - setPage("paymentdone")} - Icon={BsCurrencyDollar} - label="Payment Done" - value={done.length} - color="purple" - /> - setPage("paymentpending")} - Icon={BsCurrencyDollar} - label="Pending Payment" - value={pending.length} - color="rose" - /> -
- -
-
- Latest Referred Corporate -
- {users - .filter(referredCorporateFilter) - .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) - .map((x) => ( - - ))} -
-
-
- Latest corporate -
- {users - .filter(corporateFilter) - .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) - .map((x) => ( - - ))} -
-
-
- Referenced corporate expiring in 1 month -
- {users - .filter( - (x) => - referredCorporateFilter(x) && - moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) && - moment().isBefore(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- - ); - - return ( - <> - setSelectedUser(undefined)}> - <> - {selectedUser && ( -
- { - setSelectedUser(undefined); - if (shouldReload) reload(); - }} - onViewStudents={ - selectedUser.type === "corporate" || selectedUser.type === "teacher" ? () => setPage("students") : undefined - } - onViewTeachers={selectedUser.type === "corporate" ? () => setPage("teachers") : undefined} - user={selectedUser} - /> -
- )} - -
- {page === "referredCorporate" && } - {page === "corporate" && } - {page === "inactiveReferredCorporate" && } - {page === "paymentdone" && } - {page === "paymentpending" && } - {page === "" && } - - ); + return ( + <> + setSelectedUser(undefined)}> + <> + {selectedUser && ( +
+ { + setSelectedUser(undefined); + if (shouldReload) reload(); + }} + onViewStudents={ + selectedUser.type === "corporate" || + selectedUser.type === "teacher" + ? () => setPage("students") + : undefined + } + onViewTeachers={ + selectedUser.type === "corporate" + ? () => setPage("teachers") + : undefined + } + user={selectedUser} + /> +
+ )} + +
+ {page === "referredCorporate" && } + {page === "corporate" && } + {page === "inactiveReferredCorporate" && ( + + )} + {page === "paymentdone" && } + {page === "paymentpending" && } + {page === "" && } + + ); } diff --git a/src/dashboards/Corporate.tsx b/src/dashboards/Corporate.tsx index 49bad1e8..94d5e5ab 100644 --- a/src/dashboards/Corporate.tsx +++ b/src/dashboards/Corporate.tsx @@ -2,325 +2,395 @@ 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, } 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"; 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); +export default function CorporateDashboard({ user }: Props) { + const [page, setPage] = useState(""); + const [selectedUser, setSelectedUser] = useState(); + const [showModal, setShowModal] = 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 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]); - 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 ({users.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 +
+

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 ({users.filter(filter).length})

-
+ 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}) +

+
- 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})

-
+ 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 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!), - })); + return calculateAverageLevel(levels); + }; - 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 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" + /> + + +
- return calculateAverageLevel(levels); - }; +
+
+ 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 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" /> - - -
+ 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), + }); -
-
- 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 + } + 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), + }); - 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 === "" && } - - ); + router.push("/list/users"); + } + : undefined + } + user={selectedUser} + /> +
+ )} + +
+ {page === "students" && } + {page === "teachers" && } + {page === "groups" && } + {page === "" && } + + ); } diff --git a/src/dashboards/CorporateStudentsLevels.tsx b/src/dashboards/CorporateStudentsLevels.tsx new file mode 100644 index 00000000..698c06ae --- /dev/null +++ b/src/dashboards/CorporateStudentsLevels.tsx @@ -0,0 +1,140 @@ +import React from "react"; +import useUsers from "@/hooks/useUsers"; +import useGroups from "@/hooks/useGroups"; +import { User } from "@/interfaces/user"; +import Select from "@/components/Low/Select"; +import ProgressBar from "@/components/Low/ProgressBar"; +import { + BsBook, + BsClipboard, + BsHeadphones, + BsMegaphone, + BsPen, +} from "react-icons/bs"; +import { MODULE_ARRAY } from "@/utils/moduleUtils"; +import { capitalize } from "lodash"; +import { getLevelLabel } from "@/utils/score"; + +const Card = ({ user }: { user: User }) => { + return ( +
+
+

{user.name}

+
+
+ {MODULE_ARRAY.map((module) => { + const desiredLevel = user.desiredLevels[module] || 9; + const level = user.levels[module] || 0; + return ( +
+
+
+ {module === "reading" && ( + + )} + {module === "listening" && ( + + )} + {module === "writing" && ( + + )} + {module === "speaking" && ( + + )} + {module === "level" && ( + + )} +
+
+ + {capitalize(module)} + +
+ {module === "level" && ( + + English Level: {getLevelLabel(level).join(" / ")} + + )} + {module !== "level" && ( +
+ Level {level} / Level 9 + Desired Level: {desiredLevel} +
+ )} +
+
+
+
+ +
+
+ ); + })} +
+
+ ); +}; + +const CorporateStudentsLevels = () => { + const { users } = useUsers(); + const { groups } = useGroups(); + + const corporateUsers = users.filter((u) => u.type === "corporate") as User[]; + const [corporateId, setCorporateId] = React.useState(""); + const corporate = + corporateUsers.find((u) => u.id === corporateId) || corporateUsers[0]; + + const groupsFromCorporate = corporate + ? groups.filter((g) => g.admin === corporate.id) + : []; + + const groupsParticipants = groupsFromCorporate + .flatMap((g) => g.participants) + .reduce((accm: User[], p) => { + const user = users.find((u) => u.id === p) as User; + if (user) { + return [...accm, user]; + } + return accm; + }, []); + + return ( + <> + ({value: x.id, label: `${x.name} - ${x.email}`}))} - defaultValue={{value: user.id, label: `${user.name} - ${user.email}`}} - onChange={(value) => setStatsUserId(value?.value)} - styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), - option: (styles, state) => ({ - ...styles, - backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white", - color: state.isFocused ? "black" : styles.color, - }), - }} - /> - )} - {(user.type === "corporate" || user.type === "teacher") && groups.length > 0 && ( - x.value === selectedCorporate + )} + onChange={(value) => + setSelectedCorporate(value?.value || "") + } + styles={{ + menuPortal: (base) => ({ ...base, zIndex: 9999 }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isFocused + ? "#D5D9F0" + : state.isSelected + ? "#7872BF" + : "white", + color: state.isFocused ? "black" : styles.color, + }), + }} + > + + + + groups.flatMap((y) => y.participants).includes(x.id) + ) + .map((x) => ({ + value: x.id, + label: `${x.name} - ${x.email}`, + }))} + value={selectedUserSelectValue} + onChange={(value) => setStatsUserId(value?.value)} + styles={{ + menuPortal: (base) => ({ ...base, zIndex: 9999 }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isFocused + ? "#D5D9F0" + : state.isSelected + ? "#7872BF" + : "white", + color: state.isFocused ? "black" : styles.color, + }), + }} + /> + + )} + +
+ + + + +
+ + {groupedStats && + Object.keys(groupedStats).length > 0 && + !isStatsLoading && ( +
+ {Object.keys(filterStatsByDate(groupedStats)) + .sort((a, b) => parseInt(b) - parseInt(a)) + .map(customContent)} +
+ )} + {groupedStats && + Object.keys(groupedStats).length === 0 && + !isStatsLoading && ( + + No record to display... + + )} + + )} + + ); } diff --git a/src/pages/tickets.tsx b/src/pages/tickets.tsx index 301dfab5..ca06ff96 100644 --- a/src/pages/tickets.tsx +++ b/src/pages/tickets.tsx @@ -68,16 +68,40 @@ const SOURCE_OPTIONS = [ {value: "platform", label: "Platform"}, ]; +type CustomStatus = TicketStatus | "all" | "pending"; + +const STATUS_OPTIONS = [{ + label: 'Pending', + value: 'pending', + filter: (x: Ticket) => x.status !== 'completed', +}, { + label: 'All', + value: 'all', + filter: (x: Ticket) => true, +}, { + label: 'Completed', + value: 'completed', + filter: (x: Ticket) => x.status === 'completed', +}, { + label: 'In Progress', + value: 'in-progress', + filter: (x: Ticket) => x.status === 'in-progress', +}, { + label: 'Submitted', + value: 'submitted', + filter: (x: Ticket) => x.status === 'submitted', +}] + export default function Tickets() { const [filteredTickets, setFilteredTickets] = useState([]); const [selectedTicket, setSelectedTicket] = useState(); const [assigneeFilter, setAssigneeFilter] = useState(); const [sourceFilter, setSourceFilter] = useState(""); - + // const [statusFilter, setStatusFilter] = useState('pending'); const [dateSorting, setDateSorting] = useState<"asc" | "desc">("desc"); const [typeFilter, setTypeFilter] = useState(); - const [statusFilter, setStatusFilter] = useState(); + const [statusFilter, setStatusFilter] = useState('pending'); const {user} = useUser({redirectTo: "/login"}); const {users} = useUsers(); @@ -91,7 +115,10 @@ export default function Tickets() { const filters = []; if (user?.type === "agent") filters.push((x: Ticket) => x.assignedTo === user.id); if (typeFilter) filters.push((x: Ticket) => x.type === typeFilter); - if (statusFilter) filters.push((x: Ticket) => x.status === statusFilter); + if (statusFilter) { + const filter = STATUS_OPTIONS.find(x => x.value === statusFilter)?.filter; + if (filter) filters.push(filter); + } if (assigneeFilter) filters.push((x: Ticket) => x.assignedTo === assigneeFilter); if (sourceFilter) { if (sourceFilter === "webpage") filters.push((x: Ticket) => fromHomepage.some((r) => r.test(x.reportedFrom))); @@ -214,17 +241,9 @@ export default function Tickets() {