From d074ec390ceb8c5251edd4fb74a1908b3ea5d614 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Wed, 11 Dec 2024 11:58:52 +0000 Subject: [PATCH 1/2] ENCOA-272 --- .../AssignmentCard.tsx | 28 +- .../AssignmentView.tsx | 2 +- src/{dashboards => components}/IconCard.tsx | 0 src/components/Medium/InviteCard.tsx | 118 ++- src/components/Navbar.tsx | 30 +- src/components/UserCard.tsx | 314 +++----- src/dashboards/Admin.tsx | 647 --------------- src/dashboards/Agent.tsx | 249 ------ src/dashboards/AssignmentCreator.tsx | 575 ------------- .../Corporate/MasterStatisticalPage.tsx | 45 -- .../Corporate/StudentPerformanceList.tsx | 154 ---- .../Corporate/StudentPerformancePage.tsx | 49 -- src/dashboards/Corporate/index.tsx | 371 --------- src/dashboards/CorporateStudentsLevels.tsx | 96 --- .../MasterCorporate/MasterStatistical.tsx | 413 ---------- .../MasterCorporate/MasterStatisticalPage.tsx | 43 - .../StudentPerformanceList.tsx | 252 ------ .../StudentPerformancePage.tsx | 46 -- src/dashboards/MasterCorporate/index.tsx | 448 ----------- src/dashboards/Student.tsx | 253 ------ src/dashboards/Teacher.tsx | 326 -------- src/dashboards/views/AssignmentsPage.tsx | 184 ----- src/interfaces/user.ts | 7 - src/pages/(admin)/Lists/CodeList.tsx | 10 +- src/pages/(admin)/Lists/UserList.tsx | 742 +++++++++-------- src/pages/(register)/RegisterCorporate.tsx | 484 ++++++----- src/pages/(status)/PaymentDue.tsx | 31 +- .../api/assignments/[id]/[export]/excel.ts | 756 +++++++++--------- .../api/assignments/[id]/[export]/pdf.tsx | 26 +- src/pages/api/code/index.ts | 4 +- src/pages/api/make_user.ts | 173 ++-- src/pages/api/tickets/index.ts | 2 +- src/pages/assignments/index.tsx | 4 +- src/pages/dashboard/admin.tsx | 2 +- src/pages/dashboard/corporate.tsx | 308 +++---- src/pages/dashboard/developer.tsx | 2 +- src/pages/dashboard/mastercorporate.tsx | 2 +- src/pages/dashboard/teacher.tsx | 236 +++--- src/pages/payment-record.tsx | 196 +++-- src/pages/profile.tsx | 100 +-- src/pages/settings.tsx | 2 +- src/pages/v1/index.tsx | 199 ----- src/resources/user.ts | 8 +- src/utils/groups.be.ts | 184 ++--- src/utils/users.be.ts | 180 ++--- src/utils/users.ts | 62 +- 46 files changed, 1940 insertions(+), 6423 deletions(-) rename src/{dashboards => components}/AssignmentCard.tsx (85%) rename src/{dashboards => components}/AssignmentView.tsx (99%) rename src/{dashboards => components}/IconCard.tsx (100%) delete mode 100644 src/dashboards/Admin.tsx delete mode 100644 src/dashboards/Agent.tsx delete mode 100644 src/dashboards/AssignmentCreator.tsx delete mode 100644 src/dashboards/Corporate/MasterStatisticalPage.tsx delete mode 100644 src/dashboards/Corporate/StudentPerformanceList.tsx delete mode 100644 src/dashboards/Corporate/StudentPerformancePage.tsx delete mode 100644 src/dashboards/Corporate/index.tsx delete mode 100644 src/dashboards/CorporateStudentsLevels.tsx delete mode 100644 src/dashboards/MasterCorporate/MasterStatistical.tsx delete mode 100644 src/dashboards/MasterCorporate/MasterStatisticalPage.tsx delete mode 100644 src/dashboards/MasterCorporate/StudentPerformanceList.tsx delete mode 100644 src/dashboards/MasterCorporate/StudentPerformancePage.tsx delete mode 100644 src/dashboards/MasterCorporate/index.tsx delete mode 100644 src/dashboards/Student.tsx delete mode 100644 src/dashboards/Teacher.tsx delete mode 100644 src/dashboards/views/AssignmentsPage.tsx delete mode 100644 src/pages/v1/index.tsx diff --git a/src/dashboards/AssignmentCard.tsx b/src/components/AssignmentCard.tsx similarity index 85% rename from src/dashboards/AssignmentCard.tsx rename to src/components/AssignmentCard.tsx index 94fc1714..42bead3a 100644 --- a/src/dashboards/AssignmentCard.tsx +++ b/src/components/AssignmentCard.tsx @@ -1,18 +1,18 @@ import ProgressBar from "@/components/Low/ProgressBar"; import useUsers from "@/hooks/useUsers"; -import {Module} from "@/interfaces"; -import {Assignment} from "@/interfaces/results"; -import {calculateBandScore} from "@/utils/score"; +import { Module } from "@/interfaces"; +import { Assignment } from "@/interfaces/results"; +import { calculateBandScore } from "@/utils/score"; import clsx from "clsx"; import moment from "moment"; -import {BsBook, BsClipboard, BsHeadphones, BsMegaphone, BsPen} from "react-icons/bs"; -import {usePDFDownload} from "@/hooks/usePDFDownload"; -import {useAssignmentArchive} from "@/hooks/useAssignmentArchive"; -import {uniqBy} from "lodash"; -import {useAssignmentUnarchive} from "@/hooks/useAssignmentUnarchive"; -import {useAssignmentRelease} from "@/hooks/useAssignmentRelease"; -import {getUserName} from "@/utils/users"; -import {User} from "@/interfaces/user"; +import { BsBook, BsClipboard, BsHeadphones, BsMegaphone, BsPen } from "react-icons/bs"; +import { usePDFDownload } from "@/hooks/usePDFDownload"; +import { useAssignmentArchive } from "@/hooks/useAssignmentArchive"; +import { uniqBy } from "lodash"; +import { useAssignmentUnarchive } from "@/hooks/useAssignmentUnarchive"; +import { useAssignmentRelease } from "@/hooks/useAssignmentRelease"; +import { getUserName } from "@/utils/users"; +import { User } from "@/interfaces/user"; import { EntityWithRoles } from "@/interfaces/entity"; interface Props { @@ -71,7 +71,7 @@ export default function AssignmentCard({ // in order to be downloadable, the assignment has to be released // the component should have the allowDownload prop // and the assignment should not have the level module - return uniqModules.every(({module}) => module !== "level"); + return uniqModules.every(({ module }) => module !== "level"); } return false; @@ -82,7 +82,7 @@ export default function AssignmentCard({ // in order to be downloadable, the assignment has to be released // the component should have the allowExcelDownload prop // and the assignment should have the level module - return uniqModules.some(({module}) => module === "level"); + return uniqModules.some(({ module }) => module === "level"); } return false; @@ -121,7 +121,7 @@ export default function AssignmentCard({ {entityObj && Entity: {entityObj.label}}
- {uniqModules.map(({module}) => ( + {uniqModules.map(({ module }) => (
{formatTimestamp(stats[0].date.toString())} {timeSpent && ( <> - + • {Math.floor(timeSpent / 60)} minutes )} diff --git a/src/dashboards/IconCard.tsx b/src/components/IconCard.tsx similarity index 100% rename from src/dashboards/IconCard.tsx rename to src/components/IconCard.tsx diff --git a/src/components/Medium/InviteCard.tsx b/src/components/Medium/InviteCard.tsx index 9b3e0a20..98a89cdb 100644 --- a/src/components/Medium/InviteCard.tsx +++ b/src/components/Medium/InviteCard.tsx @@ -6,72 +6,68 @@ import { BsArrowRepeat } from "react-icons/bs"; import { toast } from "react-toastify"; interface Props { - invite: Invite; - users: User[]; - reload: () => void; + invite: Invite; + users: User[]; + reload: () => void; } export default function InviteCard({ invite, users, reload }: Props) { - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(false); - const inviter = users.find((u) => u.id === invite.from); - const name = !inviter - ? null - : inviter.type === "corporate" - ? inviter.corporateInformation?.companyInformation?.name || inviter.name - : inviter.name; + const inviter = users.find((u) => u.id === invite.from); + const name = !inviter ? null : inviter.name; - const decide = (decision: "accept" | "decline") => { - if (!confirm(`Are you sure you want to ${decision} this invite?`)) return; + const decide = (decision: "accept" | "decline") => { + if (!confirm(`Are you sure you want to ${decision} this invite?`)) return; - setIsLoading(true); - axios - .get(`/api/invites/${decision}/${invite.id}`) - .then(() => { - toast.success( - `Successfully ${decision === "accept" ? "accepted" : "declined"} the invite!`, - { toastId: "success" }, - ); - reload(); - }) - .catch((e) => { - toast.success(`Something went wrong, please try again later!`, { - toastId: "error", - }); - reload(); - }) - .finally(() => setIsLoading(false)); - }; + setIsLoading(true); + axios + .get(`/api/invites/${decision}/${invite.id}`) + .then(() => { + toast.success( + `Successfully ${decision === "accept" ? "accepted" : "declined"} the invite!`, + { toastId: "success" }, + ); + reload(); + }) + .catch((e) => { + toast.success(`Something went wrong, please try again later!`, { + toastId: "error", + }); + reload(); + }) + .finally(() => setIsLoading(false)); + }; - return ( -
- Invited by {name} -
- - -
-
- ); + return ( +
+ Invited by {name} +
+ + +
+
+ ); } diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 949257b5..f2bc5a42 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,25 +1,25 @@ -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 { Module } from "@/interfaces"; import Badge from "./Low/Badge"; -import {BsArrowRepeat, BsBook, BsCheck, BsCheckCircle, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle} from "react-icons/bs"; +import { BsArrowRepeat, BsBook, BsCheck, BsCheckCircle, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle } from "react-icons/bs"; interface Props { user: User; navDisabled?: boolean; @@ -29,7 +29,7 @@ interface Props { } /* eslint-disable @next/next/no-img-element */ -export default function Navbar({user, path, navDisabled = false, focusMode = false, onFocusLayerMouseEnter}: Props) { +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); @@ -109,9 +109,8 @@ export default function Navbar({user, path, navDisabled = false, focusMode = fal badges.map((badge) => (
+ className={`${badge.achieved ? `bg-ielts-${badge.module}` : "bg-mti-gray-anti-flash" + } flex h-8 w-8 items-center justify-center rounded-full`}> {badge.icon()}
))} @@ -145,9 +144,6 @@ export default function Navbar({user, path, navDisabled = false, focusMode = fal {user.name} - {(user.type === "corporate" || user.type === "mastercorporate") && !!user.corporateInformation?.companyInformation?.name - ? `${user.corporateInformation?.companyInformation.name} |` - : ""}{" "} {user.name} | {USER_TYPE_LABELS[user.type]} {user.type === "corporate" && !!user.demographicInformation?.position && diff --git a/src/components/UserCard.tsx b/src/components/UserCard.tsx index 977af621..dd90e5b1 100644 --- a/src/components/UserCard.tsx +++ b/src/components/UserCard.tsx @@ -1,15 +1,15 @@ import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser"; -import {CorporateInformation, CorporateUser, EMPLOYMENT_STATUS, User, Type, Stat, Gender} from "@/interfaces/user"; -import {groupBySession, averageScore} from "@/utils/stats"; -import {RadioGroup} from "@headlessui/react"; +import { CorporateInformation, CorporateUser, EMPLOYMENT_STATUS, User, Type, Stat, Gender } from "@/interfaces/user"; +import { groupBySession, averageScore } from "@/utils/stats"; +import { RadioGroup } from "@headlessui/react"; import axios from "axios"; import clsx from "clsx"; import moment from "moment"; -import {Divider} from "primereact/divider"; -import {useEffect, useState} from "react"; +import { Divider } from "primereact/divider"; +import { useEffect, useState } from "react"; import ReactDatePicker from "react-datepicker"; -import {BsFileEarmarkText, BsPencil, BsPerson, BsPersonAdd, BsStar} from "react-icons/bs"; -import {toast} from "react-toastify"; +import { BsFileEarmarkText, BsPencil, BsPerson, BsPersonAdd, BsStar } from "react-icons/bs"; +import { toast } from "react-toastify"; import Button from "./Low/Button"; import Checkbox from "./Low/Checkbox"; import CountrySelect from "./Low/CountrySelect"; @@ -17,12 +17,12 @@ import Input from "./Low/Input"; import ProfileSummary from "./ProfileSummary"; import Select from "react-select"; import useUsers from "@/hooks/useUsers"; -import {USER_TYPE_LABELS} from "@/resources/user"; -import {CURRENCIES} from "@/resources/paypal"; +import { USER_TYPE_LABELS } from "@/resources/user"; +import { CURRENCIES } from "@/resources/paypal"; import useCodes from "@/hooks/useCodes"; -import {checkAccess, getTypesOfUser} from "@/utils/permissions"; -import {PERMISSIONS} from "@/constants/userPermissions"; -import {PermissionType} from "@/interfaces/permissions"; +import { checkAccess, getTypesOfUser } from "@/utils/permissions"; +import { PERMISSIONS } from "@/constants/userPermissions"; +import { PermissionType } from "@/interfaces/permissions"; import usePermissions from "@/hooks/usePermissions"; const expirationDateColor = (date: Date) => { @@ -68,7 +68,7 @@ const USER_TYPE_OPTIONS = Object.keys(USER_TYPE_LABELS).map((type) => ({ label: USER_TYPE_LABELS[type as keyof typeof USER_TYPE_LABELS], })); -const CURRENCIES_OPTIONS = CURRENCIES.map(({label, currency}) => ({ +const CURRENCIES_OPTIONS = CURRENCIES.map(({ label, currency }) => ({ value: currency, label, })); @@ -100,9 +100,7 @@ const UserCard = ({ user.type === "corporate" || user.type === "mastercorporate" ? user.corporateInformation?.referralAgent : undefined, ); const [companyName, setCompanyName] = useState( - user.type === "corporate" || user.type === "mastercorporate" - ? user.corporateInformation?.companyInformation.name - : user.type === "agent" + user.type === "agent" ? user.agentInformation?.companyName : undefined, ); @@ -110,25 +108,19 @@ const UserCard = ({ const [commercialRegistration, setCommercialRegistration] = useState( user.type === "agent" ? user.agentInformation?.commercialRegistration : undefined, ); - const [userAmount, setUserAmount] = useState( - user.type === "corporate" || user.type === "mastercorporate" ? user.corporateInformation?.companyInformation.userAmount : undefined, - ); const [paymentValue, setPaymentValue] = useState( user.type === "corporate" || user.type === "mastercorporate" ? user.corporateInformation?.payment?.value : undefined, ); const [paymentCurrency, setPaymentCurrency] = useState( user.type === "corporate" || user.type === "mastercorporate" ? user.corporateInformation?.payment?.currency : "EUR", ); - const [monthlyDuration, setMonthlyDuration] = useState( - user.type === "corporate" || user.type === "mastercorporate" ? user.corporateInformation?.monthlyDuration : undefined, - ); const [commissionValue, setCommission] = useState( user.type === "corporate" || user.type === "mastercorporate" ? user.corporateInformation?.payment?.commission : undefined, ); - const {data: stats} = useFilterRecordsByUser(user.id); - const {users} = useUsers(); - const {codes} = useCodes(user.id); - const {permissions} = usePermissions(loggedInUser.id); + const { data: stats } = useFilterRecordsByUser(user.id); + const { users } = useUsers(); + const { codes } = useCodes(user.id); + const { permissions } = usePermissions(loggedInUser.id); useEffect(() => { if (users && users.length > 0) { @@ -153,7 +145,7 @@ const UserCard = ({ if (!confirm(`Are you sure you want to update ${user.name}'s account?`)) return; axios - .post<{user?: User; ok?: boolean}>(`/api/users/update?id=${user.id}`, { + .post<{ user?: User; ok?: boolean }>(`/api/users/update?id=${user.id}`, { ...user, subscriptionExpirationDate: expiryDate, studentID, @@ -167,26 +159,21 @@ const UserCard = ({ agentInformation: type === "agent" ? { - companyName, - commercialRegistration, - arabName, - } + companyName, + commercialRegistration, + arabName, + } : undefined, corporateInformation: type === "corporate" || type === "mastercorporate" ? { - referralAgent, - monthlyDuration, - companyInformation: { - name: companyName, - userAmount, - }, - payment: { - value: paymentValue, - currency: paymentCurrency, - ...(referralAgent === "" ? {} : {commission: commissionValue}), - }, - } + referralAgent, + payment: { + value: paymentValue, + currency: paymentCurrency, + ...(referralAgent === "" ? {} : { commission: commissionValue }), + }, + } : undefined, }) .then(() => { @@ -194,7 +181,7 @@ const UserCard = ({ onClose(true); }) .catch(() => { - toast.error("Something went wrong!", {toastId: "update-error"}); + toast.error("Something went wrong!", { toastId: "update-error" }); }); }; @@ -216,31 +203,16 @@ const UserCard = ({ }, ]; - const corporateProfileItems = - user.type === "corporate" || user.type === "mastercorporate" - ? [ - { - icon: , - value: codes.length, - label: "Users Used", - }, - { - icon: , - value: user.corporateInformation?.companyInformation?.userAmount, - label: "Number of Users", - }, - ] - : []; - const updateUserPermission = PERMISSIONS.updateUser[user.type] as { list: Type[]; perm: PermissionType; }; + return ( <> {user.type === "agent" && ( @@ -283,48 +255,6 @@ const UserCard = ({ {(user.type === "corporate" || user.type === "mastercorporate") && ( <>
- - setUserAmount(e ? parseInt(e) : undefined)} - placeholder="Enter number of users" - defaultValue={userAmount} - disabled={ - disabled || - checkAccess( - loggedInUser, - getTypesOfUser(["developer", "admin", ...((user.type === "corporate" ? ["mastercorporate"] : []) as Type[])]), - ) - } - /> - setMonthlyDuration(e ? parseInt(e) : undefined)} - placeholder="Enter monthly duration" - defaultValue={monthlyDuration} - disabled={disabled || checkAccess(loggedInUser, getTypesOfUser(["developer", "admin"]))} - />
@@ -346,7 +276,7 @@ const UserCard = ({ onChange={(value) => setPaymentCurrency(value?.value)} menuPortalTarget={document?.body} styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), + menuPortal: (base) => ({ ...base, zIndex: 9999 }), control: (styles) => ({ ...styles, paddingLeft: "4px", @@ -375,10 +305,10 @@ const UserCard = ({ className={clsx( "px-4 py-4 w-full text-sm font-normal placeholder:text-mti-gray-cool bg-white rounded-full border border-mti-gray-platinum focus:outline-none", (checkAccess(loggedInUser, getTypesOfUser(["developer", "admin"])) || disabledFields.countryManager) && - "!bg-mti-gray-platinum/40 !text-mti-gray-dim cursor-not-allowed", + "!bg-mti-gray-platinum/40 !text-mti-gray-dim cursor-not-allowed", )} options={[ - {value: "", label: "No referral"}, + { value: "", label: "No referral" }, ...users .filter((u) => u.type === "agent") .map((x) => ({ @@ -393,7 +323,7 @@ const UserCard = ({ menuPortalTarget={document?.body} onChange={(value) => setReferralAgent(value?.value)} styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), + menuPortal: (base) => ({ ...base, zIndex: 9999 }), control: (styles) => ({ ...styles, paddingLeft: "4px", @@ -505,9 +435,9 @@ const UserCard = ({ value={user.demographicInformation?.employment} className="grid grid-cols-2 items-center gap-4 place-items-center" disabled={disabled}> - {EMPLOYMENT_STATUS.map(({status, label}) => ( + {EMPLOYMENT_STATUS.map(({ status, label }) => ( - {({checked}) => ( + {({ checked }) => ( - {({checked}) => ( + {({ checked }) => ( - {({checked}) => ( + {({ checked }) => ( - {({checked}) => ( + {({ checked }) => ( - -
-
- - { + if (checkAccess(loggedInUser, ["admin", "developer"])) return true; + return x.value !== "paymentDue"; + })} + menuPortalTarget={document?.body} + value={USER_STATUS_OPTIONS.find((o) => o.value === status)} + onChange={(value) => setStatus(value?.value as typeof user.status)} + styles={{ + control: (styles) => ({ + ...styles, + paddingLeft: "4px", + border: "none", outline: "none", - }, - }), - menuPortal: (base) => ({...base, zIndex: 9999}), - option: (styles, state) => ({ - ...styles, - backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white", - color: state.isFocused ? "black" : styles.color, - }), - }} - isDisabled={disabled} - /> -
-
- - { + if (x.value === "student") + return checkAccess( + loggedInUser, + ["developer", "admin", "corporate", "mastercorporate"], + permissions, + "editStudent", + ); - if (x.value === "teacher") - return checkAccess( - loggedInUser, - ["developer", "admin", "corporate", "mastercorporate"], - permissions, - "editTeacher", - ); + if (x.value === "teacher") + return checkAccess( + loggedInUser, + ["developer", "admin", "corporate", "mastercorporate"], + permissions, + "editTeacher", + ); - if (x.value === "corporate") - return checkAccess(loggedInUser, ["developer", "admin", "mastercorporate"], permissions, "editCorporate"); + if (x.value === "corporate") + return checkAccess(loggedInUser, ["developer", "admin", "mastercorporate"], permissions, "editCorporate"); - return checkAccess(loggedInUser, ["developer", "admin"]); - })} - menuPortalTarget={document?.body} - value={USER_TYPE_OPTIONS.find((o) => o.value === type)} - onChange={(value) => setType(value?.value as typeof user.type)} - styles={{ - control: (styles) => ({ - ...styles, - paddingLeft: "4px", - border: "none", - outline: "none", - ":focus": { + return checkAccess(loggedInUser, ["developer", "admin"]); + })} + menuPortalTarget={document?.body} + value={USER_TYPE_OPTIONS.find((o) => o.value === type)} + onChange={(value) => setType(value?.value as typeof user.type)} + styles={{ + control: (styles) => ({ + ...styles, + paddingLeft: "4px", + border: "none", outline: "none", - }, - }), - menuPortal: (base) => ({...base, zIndex: 9999}), - option: (styles, state) => ({ - ...styles, - backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white", - color: state.isFocused ? "black" : styles.color, - }), - }} - isDisabled={disabled} - /> + ":focus": { + outline: "none", + }, + }), + menuPortal: (base) => ({ ...base, zIndex: 9999 }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white", + color: state.isFocused ? "black" : styles.color, + }), + }} + isDisabled={disabled} + /> +
-
- - )} + + )}
diff --git a/src/dashboards/Admin.tsx b/src/dashboards/Admin.tsx deleted file mode 100644 index 656a4377..00000000 --- a/src/dashboards/Admin.tsx +++ /dev/null @@ -1,647 +0,0 @@ -/* eslint-disable @next/next/no-img-element */ -import Modal from "@/components/Modal"; -import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser"; -import useUsers from "@/hooks/useUsers"; -import {Stat, User} from "@/interfaces/user"; -import UserList from "@/pages/(admin)/Lists/UserList"; -import {dateSorter} from "@/utils"; -import moment from "moment"; -import {useEffect, useState} from "react"; -import { - 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 usePaymentStatusUsers from "@/hooks/usePaymentStatusUsers"; -import CorporateStudentsLevels from "./CorporateStudentsLevels"; - -interface Props { - user: User; -} - -const studentHash = { - type: "student", - size: 25, - orderBy: "registrationDate", -}; - -const teacherHash = { - type: "teacher", - size: 25, - orderBy: "registrationDate", -}; - -const corporateHash = { - type: "corporate", - size: 25, - orderBy: "registrationDate", -}; - -const agentsHash = { - type: "agent", - size: 25, - orderBy: "registrationDate", -}; - -export default function AdminDashboard({user}: Props) { - const [page, setPage] = useState(""); - const [selectedUser, setSelectedUser] = useState(); - const [showModal, setShowModal] = useState(false); - - const {users: students, total: totalStudents, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers(studentHash); - const {users: teachers, total: totalTeachers, reload: reloadTeachers, isLoading: isTeachersLoading} = useUsers(teacherHash); - const {users: corporates, total: totalCorporate, reload: reloadCorporates, isLoading: isCorporatesLoading} = useUsers(corporateHash); - const {users: agents, total: totalAgents, reload: reloadAgents, isLoading: isAgentsLoading} = useUsers(agentsHash); - - const {groups} = useGroups({}); - const {pending, done} = usePaymentStatusUsers(); - - const appendUserFilters = useFilterStore((state) => state.appendUserFilter); - const router = useRouter(); - - useEffect(() => { - setShowModal(!!selectedUser && router.asPath === "/#"); - }, [selectedUser, router.asPath]); - - const inactiveCountryManagerFilter = (x: User) => 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 StudentsList = () => { - const filter = (x: User) => - !!selectedUser - ? groups - .filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id)) - .flatMap((g) => g.participants) - .includes(x.id) - : true; - - return ( - ( -
-
router.push("/")} - 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) => - !!selectedUser - ? groups - .filter((g) => g.admin === selectedUser.id || g.participants.includes(selectedUser.id)) - .flatMap((g) => g.participants) - .includes(x.id) || false - : true; - - return ( - ( -
-
router.push("/")} - 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 AgentsList = () => { - return ( - ( -
-
router.push("/")} - 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 CorporateList = () => ( - ( -
-
router.push("/")} - 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 CorporatePaidStatusList = ({paid}: {paid: Boolean}) => { - const list = paid ? done : pending; - const filter = (x: User) => list.includes(x.id); - - return ( - ( -
-
router.push("/")} - 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 InactiveCountryManagerList = () => { - return ( - ( -
-
router.push("/")} - 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})

-
- )} - /> - ); - }; - - const InactiveStudentsList = () => { - const filter = (x: User) => x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate); - - return ( - ( -
-
router.push("/")} - 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})

-
- )} - /> - ); - }; - - const InactiveCorporateList = () => { - const filter = (x: User) => x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate); - - return ( - ( -
-
router.push("/")} - 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 ( - <> -
-
router.push("/")} - 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 DefaultDashboard = () => ( - <> -
- router.push("/#students")} - color="purple" - /> - router.push("/#teachers")} - color="purple" - /> - router.push("/#corporate")} - color="purple" - /> - router.push("/#agents")} - color="purple" - /> - x.demographicInformation).map((x) => x.demographicInformation?.country))].length} - color="purple" - /> - router.push("/#inactiveStudents")} - Icon={BsPersonFill} - isLoading={isStudentsLoading} - label="Inactive Students" - value={ - students.filter((x) => x.type === "student" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate))) - .length - } - color="rose" - /> - router.push("/#inactiveCountryManagers")} - Icon={BsBriefcaseFill} - isLoading={isAgentsLoading} - label="Inactive Country Managers" - value={agents.filter(inactiveCountryManagerFilter).length} - color="rose" - /> - router.push("/#inactiveCorporate")} - Icon={BsBank} - isLoading={isCorporatesLoading} - label="Inactive Corporate" - value={ - corporates.filter( - (x) => x.type === "corporate" && (x.status === "disabled" || moment().isAfter(x.subscriptionExpirationDate)), - ).length - } - color="rose" - /> - router.push("/#paymentdone")} - Icon={BsCurrencyDollar} - label="Payment Done" - value={done.length} - color="purple" - /> - router.push("/#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" - /> - router.push("/#corporatestudentslevels")} - Icon={BsPersonFill} - isLoading={isStudentsLoading} - label="Corporate Students Levels" - color="purple" - /> -
- -
-
- Latest students -
- {students - .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) - .map((x) => ( - - ))} -
-
-
- Latest teachers -
- {teachers - .sort((a, b) => { - return dateSorter(a, b, "desc", "registrationDate"); - }) - .map((x) => ( - - ))} -
-
-
- Latest corporate -
- {corporates - .sort((a, b) => { - return dateSorter(a, b, "desc", "registrationDate"); - }) - .map((x) => ( - - ))} -
-
-
- Unpaid Corporate -
- {corporates - .filter((x) => x.status === "paymentDue") - .map((x) => ( - - ))} -
-
-
- Students expiring in 1 month -
- {students - .filter( - (x) => - x.subscriptionExpirationDate && - moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) && - moment().isBefore(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Teachers expiring in 1 month -
- {teachers - .filter( - (x) => - x.subscriptionExpirationDate && - moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) && - moment().isBefore(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Country Manager expiring in 1 month -
- {agents - .filter( - (x) => - x.subscriptionExpirationDate && - moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) && - moment().isBefore(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Corporate expiring in 1 month -
- {corporates - .filter( - (x) => - x.subscriptionExpirationDate && - moment().isAfter(moment(x.subscriptionExpirationDate).subtract(30, "days")) && - moment().isBefore(moment(x.subscriptionExpirationDate)), - ) - .map((x) => ( - - ))} -
-
-
- Expired Students -
- {students - .filter((x) => x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate))) - .map((x) => ( - - ))} -
-
-
- Expired Teachers -
- {teachers - .filter((x) => x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate))) - .map((x) => ( - - ))} -
-
-
- Expired Country Manager -
- {agents - .filter((x) => x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate))) - .map((x) => ( - - ))} -
-
-
- Expired Corporate -
- {corporates - .filter((x) => x.subscriptionExpirationDate && moment().isAfter(moment(x.subscriptionExpirationDate))) - .map((x) => ( - - ))} -
-
-
- - ); - - return ( - <> - setSelectedUser(undefined)}> - <> - {selectedUser && ( -
- { - setSelectedUser(undefined); - if (shouldReload && selectedUser!.type === "student") reloadStudents(); - if (shouldReload && selectedUser!.type === "teacher") reloadTeachers(); - if (shouldReload && selectedUser!.type === "corporate") reloadCorporates(); - if (shouldReload && selectedUser!.type === "agent") reloadAgents(); - }} - 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("/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("/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("/users"); - } - : undefined - } - user={selectedUser} - /> -
- )} - -
- {router.asPath === "/#students" && } - {router.asPath === "/#teachers" && } - {router.asPath === "/#corporate" && } - {router.asPath === "/#agents" && } - {router.asPath === "/#inactiveStudents" && } - {router.asPath === "/#inactiveCorporate" && } - {router.asPath === "/#inactiveCountryManagers" && } - {router.asPath === "/#paymentdone" && } - {router.asPath === "/#paymentpending" && } - {router.asPath === "/#corporatestudentslevels" && } - {router.asPath === "/" && } - - ); -} diff --git a/src/dashboards/Agent.tsx b/src/dashboards/Agent.tsx deleted file mode 100644 index ce5ff763..00000000 --- a/src/dashboards/Agent.tsx +++ /dev/null @@ -1,249 +0,0 @@ -/* eslint-disable @next/next/no-img-element */ -import Modal from "@/components/Modal"; -import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser"; -import useUsers from "@/hooks/useUsers"; -import {Stat, User} from "@/interfaces/user"; -import UserList from "@/pages/(admin)/Lists/UserList"; -import {dateSorter} from "@/utils"; -import moment from "moment"; -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"; - -interface Props { - user: User; -} - -export default function AgentDashboard({user}: Props) { - const [page, setPage] = useState(""); - const [selectedUser, setSelectedUser] = useState(); - const [showModal, setShowModal] = useState(false); - - const {data: stats} = useFilterRecordsByUser(); - const {users, reload} = useUsers(); - const {pending, done} = usePaymentStatusUsers(); - - 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 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 ({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 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 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 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 === "" && } - - ); -} diff --git a/src/dashboards/AssignmentCreator.tsx b/src/dashboards/AssignmentCreator.tsx deleted file mode 100644 index 89c58598..00000000 --- a/src/dashboards/AssignmentCreator.tsx +++ /dev/null @@ -1,575 +0,0 @@ -import Input from "@/components/Low/Input"; -import Modal from "@/components/Modal"; -import { Module } from "@/interfaces"; -import clsx from "clsx"; -import { useEffect, useMemo, useState } from "react"; -import { BsBook, BsCheckCircle, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle } from "react-icons/bs"; -import { generate } from "random-words"; -import { capitalize } from "lodash"; -import useUsers from "@/hooks/useUsers"; -import { Group, User } from "@/interfaces/user"; -import ProgressBar from "@/components/Low/ProgressBar"; -import { calculateAverageLevel } from "@/utils/score"; -import Button from "@/components/Low/Button"; -import ReactDatePicker from "react-datepicker"; -import moment from "moment"; -import axios from "axios"; -import { getExam } from "@/utils/exams"; -import { toast } from "react-toastify"; -import { Assignment } from "@/interfaces/results"; -import Checkbox from "@/components/Low/Checkbox"; -import { InstructorGender, Variant } from "@/interfaces/exam"; -import Select from "@/components/Low/Select"; -import useExams from "@/hooks/useExams"; -import { useListSearch } from "@/hooks/useListSearch"; - -interface Props { - isCreating: boolean; - users: User[]; - user: User; - groups: Group[]; - assignment?: Assignment; - cancelCreation: () => void; -} - -const SIZE = 12; - -export default function AssignmentCreator({ isCreating, assignment, user, groups, users, cancelCreation }: Props) { - const [studentsPage, setStudentsPage] = useState(0); - const [teachersPage, setTeachersPage] = useState(0); - - const [selectedModules, setSelectedModules] = useState(assignment?.exams.map((e) => e.module) || []); - const [assignees, setAssignees] = useState(assignment?.assignees || []); - const [teachers, setTeachers] = useState(!!assignment ? assignment.teachers || [] : [...(user.type === "teacher" ? [user.id] : [])]); - const [name, setName] = useState( - assignment?.name || - generate({ - minLength: 6, - maxLength: 8, - min: 2, - max: 3, - join: " ", - formatter: capitalize, - }), - ); - const [isLoading, setIsLoading] = useState(false); - const [startDate, setStartDate] = useState(assignment ? moment(assignment.startDate).toDate() : moment().add(1, "hour").toDate()); - - const [endDate, setEndDate] = useState( - assignment ? moment(assignment.endDate).toDate() : moment().hours(23).minutes(59).add(8, "day").toDate(), - ); - const [variant, setVariant] = useState("full"); - const [instructorGender, setInstructorGender] = useState(assignment?.instructorGender || "varied"); - // creates a new exam for each assignee or just one exam for all assignees - const [generateMultiple, setGenerateMultiple] = useState(false); - const [released, setReleased] = useState(assignment?.released || false); - - const [autoStart, setAutostart] = useState(assignment?.autoStart || false); - - const [useRandomExams, setUseRandomExams] = useState(true); - const [examIDs, setExamIDs] = useState<{ id: string; module: Module }[]>([]); - - const { exams } = useExams(); - - const userStudents = useMemo(() => users.filter((x) => x.type === "student"), [users]); - const userTeachers = useMemo(() => users.filter((x) => x.type === "teacher"), [users]); - - const { rows: filteredStudentsRows, renderSearch: renderStudentSearch, text: studentText } = useListSearch([["name"], ["email"]], userStudents); - const { rows: filteredTeachersRows, renderSearch: renderTeacherSearch, text: teacherText } = useListSearch([["name"], ["email"]], userTeachers); - - useEffect(() => setStudentsPage(0), [studentText]); - const studentRows = useMemo( - () => - filteredStudentsRows.slice( - studentsPage * SIZE, - (studentsPage + 1) * SIZE > filteredStudentsRows.length ? filteredStudentsRows.length : (studentsPage + 1) * SIZE, - ), - [filteredStudentsRows, studentsPage], - ); - - useEffect(() => setTeachersPage(0), [teacherText]); - const teacherRows = useMemo( - () => - filteredTeachersRows.slice( - teachersPage * SIZE, - (teachersPage + 1) * SIZE > filteredTeachersRows.length ? filteredTeachersRows.length : (teachersPage + 1) * SIZE, - ), - [filteredTeachersRows, teachersPage], - ); - - useEffect(() => { - setExamIDs((prev) => prev.filter((x) => selectedModules.includes(x.module))); - }, [selectedModules]); - - const toggleModule = (module: Module) => { - const modules = selectedModules.filter((x) => x !== module); - setSelectedModules((prev) => (prev.includes(module) ? modules : [...modules, module])); - }; - - const toggleAssignee = (user: User) => { - setAssignees((prev) => (prev.includes(user.id) ? prev.filter((a) => a !== user.id) : [...prev, user.id])); - }; - - const toggleTeacher = (user: User) => { - setTeachers((prev) => (prev.includes(user.id) ? prev.filter((a) => a !== user.id) : [...prev, user.id])); - }; - - const createAssignment = () => { - setIsLoading(true); - - (assignment ? axios.patch : axios.post)(`/api/assignments${assignment ? `/${assignment.id}` : ""}`, { - assignees, - name, - startDate, - examIDs: !useRandomExams ? examIDs : undefined, - endDate, - selectedModules, - generateMultiple, - teachers, - variant, - instructorGender, - released, - autoStart, - }) - .then(() => { - toast.success(`The assignment "${name}" has been ${assignment ? "updated" : "created"} successfully!`); - cancelCreation(); - }) - .catch((e) => { - console.log(e); - toast.error("Something went wrong, please try again later!"); - }) - .finally(() => setIsLoading(false)); - }; - - const deleteAssignment = () => { - if (assignment) { - setIsLoading(true); - - if (!confirm(`Are you sure you want to delete the "${assignment.name}" assignment?`)) return; - axios - .delete(`api/assignments/${assignment.id}`) - .then(() => { - toast.success(`The assignment "${name}" has been deleted successfully!`); - cancelCreation(); - }) - .catch((e) => { - console.log(e); - toast.error("Something went wrong, please try again later!"); - }) - .finally(() => setIsLoading(false)); - } - }; - - const startAssignment = () => { - if (assignment) { - setIsLoading(true); - - axios - .post(`/api/assignments/${assignment.id}/start`) - .then(() => { - toast.success(`The assignment "${name}" has been started successfully!`); - cancelCreation(); - }) - .catch((e) => { - console.log(e); - toast.error("Something went wrong, please try again later!"); - }) - .finally(() => setIsLoading(false)); - } - }; - - return ( - -
-
-
toggleModule("reading") : undefined} - className={clsx( - "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer", - selectedModules.includes("reading") ? "border-mti-purple-light" : "border-mti-gray-platinum", - )}> -
- -
- Reading - {!selectedModules.includes("reading") && !selectedModules.includes("level") && ( -
- )} - {selectedModules.includes("level") && } - {selectedModules.includes("reading") && } -
-
toggleModule("listening") : undefined} - className={clsx( - "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer", - selectedModules.includes("listening") ? "border-mti-purple-light" : "border-mti-gray-platinum", - )}> -
- -
- Listening - {!selectedModules.includes("listening") && !selectedModules.includes("level") && ( -
- )} - {selectedModules.includes("level") && } - {selectedModules.includes("listening") && } -
-
toggleModule("level") - : undefined - } - className={clsx( - "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer", - selectedModules.includes("level") ? "border-mti-purple-light" : "border-mti-gray-platinum", - )}> -
- -
- Level - {!selectedModules.includes("level") && selectedModules.length === 0 && ( -
- )} - {!selectedModules.includes("level") && selectedModules.length > 0 && } - {selectedModules.includes("level") && } -
-
toggleModule("writing") : undefined} - className={clsx( - "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer", - selectedModules.includes("writing") ? "border-mti-purple-light" : "border-mti-gray-platinum", - )}> -
- -
- Writing - {!selectedModules.includes("writing") && !selectedModules.includes("level") && ( -
- )} - {selectedModules.includes("level") && } - {selectedModules.includes("writing") && } -
-
toggleModule("speaking") : undefined} - className={clsx( - "w-52 relative max-w-xs flex items-center bg-mti-white-alt transition duration-300 ease-in-out border p-5 rounded-xl gap-8 cursor-pointer", - selectedModules.includes("speaking") ? "border-mti-purple-light" : "border-mti-gray-platinum", - )}> -
- -
- Speaking - {!selectedModules.includes("speaking") && !selectedModules.includes("level") && ( -
- )} - {selectedModules.includes("level") && } - {selectedModules.includes("speaking") && } -
-
- - setName(e)} defaultValue={name} label="Assignment Name" required /> - -
-
- - moment(date).isSameOrAfter(new Date())} - dateFormat="dd/MM/yyyy HH:mm" - selected={startDate} - showTimeSelect - onChange={(date) => setStartDate(date)} - /> -
-
- - moment(date).isAfter(startDate)} - dateFormat="dd/MM/yyyy HH:mm" - selected={endDate} - showTimeSelect - onChange={(date) => setEndDate(date)} - /> -
-
- - {selectedModules.includes("speaking") && ( -
- - e.module === module)?.id || null, - label: examIDs.find((e) => e.module === module)?.id || "", - }} - onChange={(value) => - value - ? setExamIDs((prev) => [...prev.filter((x) => x.module !== module), { id: value.value!, module }]) - : setExamIDs((prev) => prev.filter((x) => x.module !== module)) - } - options={exams - .filter((x) => !x.isDiagnostic && x.module === module) - .map((x) => ({ value: x.id, label: x.id }))} - /> -
- ))} -
- )} -
- )} - -
- Assignees ({assignees.length} selected) -
- {groups.map((g) => ( - - ))} -
- - {renderStudentSearch()} - -
- {studentRows.map((user) => ( -
toggleAssignee(user)} - className={clsx( - "p-4 flex flex-col gap-2 rounded-xl border cursor-pointer w-72", - "transition ease-in-out duration-300", - assignees.includes(user.id) ? "border-mti-purple" : "border-mti-gray-platinum", - )} - key={user.id}> - - {user.name} - {user.email} - - - - Groups:{" "} - {groups - .filter((g) => g.participants.includes(user.id)) - .map((g) => g.name) - .join(", ")} - -
- ))} -
-
-
- -
-
- - {studentsPage * SIZE + 1} -{" "} - {(studentsPage + 1) * SIZE > filteredStudentsRows.length ? filteredStudentsRows.length : (studentsPage + 1) * SIZE} /{" "} - {filteredStudentsRows.length} - - -
-
-
- - {user.type !== "teacher" && ( -
- Teachers ({teachers.length} selected) -
- {groups.map((g) => ( - - ))} -
- - {renderTeacherSearch()} - -
- {teacherRows.map((user) => ( -
toggleTeacher(user)} - className={clsx( - "p-4 flex flex-col gap-2 rounded-xl border cursor-pointer w-72", - "transition ease-in-out duration-300", - teachers.includes(user.id) ? "border-mti-purple" : "border-mti-gray-platinum", - )} - key={user.id}> - - {user.name} - {user.email} - - - Groups:{" "} - {groups - .filter((g) => g.participants.includes(user.id)) - .map((g) => g.name) - .join(", ")} - -
- ))} -
- -
-
- -
-
- - {teachersPage * SIZE + 1} -{" "} - {(teachersPage + 1) * SIZE > filteredTeachersRows.length - ? filteredTeachersRows.length - : (teachersPage + 1) * SIZE}{" "} - / {filteredTeachersRows.length} - - -
-
-
- )} - -
- setVariant((prev) => (prev === "full" ? "partial" : "full"))}> - Full length exams - - setGenerateMultiple((d) => !d)}> - Generate different exams - - setReleased((d) => !d)}> - Auto release results - - setAutostart((d) => !d)}> - Auto start exam - -
-
- - {assignment && ( - <> - - - - )} - -
-
- - ); -} diff --git a/src/dashboards/Corporate/MasterStatisticalPage.tsx b/src/dashboards/Corporate/MasterStatisticalPage.tsx deleted file mode 100644 index 14420ae4..00000000 --- a/src/dashboards/Corporate/MasterStatisticalPage.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import useUsers, {userHashStudent, userHashTeacher} from "@/hooks/useUsers"; -import {CorporateUser, User} from "@/interfaces/user"; -import {useRouter} from "next/router"; -import {useMemo} from "react"; -import {BsArrowLeft} from "react-icons/bs"; -import MasterStatistical from "../MasterCorporate/MasterStatistical"; - -interface Props { - user: CorporateUser; -} - -const MasterStatisticalPage = ({user}: Props) => { - const {users: students} = useUsers(userHashStudent); - const {users: teachers} = useUsers(userHashTeacher); - - // this workaround will allow us toreuse the master statistical due to master corporate restraints - // while still being able to use the corporate user - const groupedByNameCorporateIds = useMemo( - () => ({ - [user.corporateInformation?.companyInformation?.name || user.name]: [user.id], - }), - [user], - ); - - const teachersAndStudents = useMemo(() => [...students, ...teachers], [students, teachers]); - - const router = useRouter(); - - return ( - <> -
-
router.push("/")} - className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"> - - Back -
-

Master Statistical

-
- - - ); -}; - -export default MasterStatisticalPage; diff --git a/src/dashboards/Corporate/StudentPerformanceList.tsx b/src/dashboards/Corporate/StudentPerformanceList.tsx deleted file mode 100644 index ca0e920a..00000000 --- a/src/dashboards/Corporate/StudentPerformanceList.tsx +++ /dev/null @@ -1,154 +0,0 @@ -/* eslint-disable @next/next/no-img-element */ -import Modal from "@/components/Modal"; -import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser"; -import useUsers, {userHashStudent, userHashTeacher, userHashCorporate} from "@/hooks/useUsers"; -import {CorporateUser, Group, MasterCorporateUser, Stat, User} from "@/interfaces/user"; -import UserList from "@/pages/(admin)/Lists/UserList"; -import {dateSorter} from "@/utils"; -import moment from "moment"; -import {useEffect, useMemo, useState} from "react"; -import { - 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 {averageLevelCalculator, 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 useCodes from "@/hooks/useCodes"; -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"; -import {createColumnHelper} from "@tanstack/react-table"; -import Checkbox from "@/components/Low/Checkbox"; -import List from "@/components/List"; -import {getUserCompanyName} from "@/resources/user"; -import {futureAssignmentFilter, pastAssignmentFilter, archivedAssignmentFilter, activeAssignmentFilter} from "@/utils/assignments"; -import useUserBalance from "@/hooks/useUserBalance"; -import AssignmentsPage from "../views/AssignmentsPage"; - -type StudentPerformanceItem = User & {corporateName: string; group: string}; -const StudentPerformanceList = ({items, stats, users}: {items: StudentPerformanceItem[]; stats: Stat[]; users: User[]}) => { - const [isShowingAmount, setIsShowingAmount] = useState(false); - - const columnHelper = createColumnHelper(); - - const columns = [ - columnHelper.accessor("name", { - header: "Student Name", - cell: (info) => info.getValue(), - }), - columnHelper.accessor("email", { - header: "E-mail", - cell: (info) => info.getValue(), - }), - columnHelper.accessor("demographicInformation.passport_id", { - header: "ID", - cell: (info) => info.getValue() || "N/A", - }), - columnHelper.accessor("group", { - header: "Group", - cell: (info) => info.getValue(), - }), - columnHelper.accessor("corporateName", { - header: "Corporate", - cell: (info) => info.getValue() || "N/A", - }), - columnHelper.accessor("levels.reading", { - header: "Reading", - cell: (info) => - !isShowingAmount - ? info.getValue() || 0 - : `${Object.keys(groupByExam(stats.filter((x) => x.module === "reading" && x.user === info.row.original.id))).length} exams`, - }), - columnHelper.accessor("levels.listening", { - header: "Listening", - cell: (info) => - !isShowingAmount - ? info.getValue() || 0 - : `${Object.keys(groupByExam(stats.filter((x) => x.module === "listening" && x.user === info.row.original.id))).length} exams`, - }), - columnHelper.accessor("levels.writing", { - header: "Writing", - cell: (info) => - !isShowingAmount - ? info.getValue() || 0 - : `${Object.keys(groupByExam(stats.filter((x) => x.module === "writing" && x.user === info.row.original.id))).length} exams`, - }), - columnHelper.accessor("levels.speaking", { - header: "Speaking", - cell: (info) => - !isShowingAmount - ? info.getValue() || 0 - : `${Object.keys(groupByExam(stats.filter((x) => x.module === "speaking" && x.user === info.row.original.id))).length} exams`, - }), - columnHelper.accessor("levels.level", { - header: "Level", - cell: (info) => - !isShowingAmount - ? info.getValue() || 0 - : `${Object.keys(groupByExam(stats.filter((x) => x.module === "level" && x.user === info.row.original.id))).length} exams`, - }), - columnHelper.accessor("levels", { - id: "overall_level", - header: "Overall", - cell: (info) => - !isShowingAmount - ? averageLevelCalculator( - users, - stats.filter((x) => x.user === info.row.original.id), - ).toFixed(1) - : `${Object.keys(groupByExam(stats.filter((x) => x.user === info.row.original.id))).length} exams`, - }), - ]; - - return ( -
- - Show Utilization - - - data={items.sort( - (a, b) => - averageLevelCalculator( - users, - stats.filter((x) => x.user === b.id), - ) - - averageLevelCalculator( - users, - stats.filter((x) => x.user === a.id), - ), - )} - columns={columns} - /> -
- ); -}; - -export default StudentPerformanceList; diff --git a/src/dashboards/Corporate/StudentPerformancePage.tsx b/src/dashboards/Corporate/StudentPerformancePage.tsx deleted file mode 100644 index 21092ac2..00000000 --- a/src/dashboards/Corporate/StudentPerformancePage.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser"; -import useGroups from "@/hooks/useGroups"; -import useUsers, {userHashStudent} from "@/hooks/useUsers"; -import {Stat, User} from "@/interfaces/user"; -import {getUserCompanyName} from "@/resources/user"; -import clsx from "clsx"; -import {useRouter} from "next/router"; -import {BsArrowLeft, BsArrowRepeat} from "react-icons/bs"; -import StudentPerformanceList from "./StudentPerformanceList"; - -interface Props { - user: User; -} - -const StudentPerformancePage = ({user}: Props) => { - const {groups} = useGroups({admin: user.id}); - const {users: students, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers(userHashStudent); - const {data: stats} = useFilterRecordsByUser(); - - const router = useRouter(); - - const performanceStudents = students.map((u) => ({ - ...u, - group: groups.find((x) => x.participants.includes(u.id))?.name || "N/A", - corporateName: getUserCompanyName(user, [], groups), - })); - - return ( - <> -
-
router.push("/")} - 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 - -
-
- - - ); -}; - -export default StudentPerformancePage; diff --git a/src/dashboards/Corporate/index.tsx b/src/dashboards/Corporate/index.tsx deleted file mode 100644 index b21aaf28..00000000 --- a/src/dashboards/Corporate/index.tsx +++ /dev/null @@ -1,371 +0,0 @@ -/* eslint-disable @next/next/no-img-element */ -import Modal from "@/components/Modal"; -import useFilterRecordsByUser from "@/hooks/useFilterRecordsByUser"; -import useUsers from "@/hooks/useUsers"; -import {CorporateUser, Group, MasterCorporateUser, Stat, User} from "@/interfaces/user"; -import UserList from "@/pages/(admin)/Lists/UserList"; -import {dateSorter, mapBy} from "@/utils"; -import moment from "moment"; -import {useEffect, useMemo, useState} from "react"; -import { - BsArrowLeft, - BsClipboard2Data, - BsClock, - BsPaperclip, - BsPersonFill, - BsPersonFillGear, - BsPencilSquare, - BsPersonCheck, - BsPeople, - BsEnvelopePaper, - BsDatabase, -} from "react-icons/bs"; -import UserCard from "@/components/UserCard"; -import useGroups from "@/hooks/useGroups"; -import {calculateAverageLevel, calculateBandScore} from "@/utils/score"; -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 useAssignments from "@/hooks/useAssignments"; -import useUserBalance from "@/hooks/useUserBalance"; -import AssignmentsPage from "../views/AssignmentsPage"; -import StudentPerformancePage from "./StudentPerformancePage"; -import MasterStatisticalPage from "./MasterStatisticalPage"; -import {getEntitiesUsers} from "@/utils/users.be"; - -interface Props { - user: CorporateUser; - linkedCorporate?: CorporateUser | MasterCorporateUser; -} - -const studentHash = { - type: "student", - orderBy: "registrationDate", - size: 25, -}; - -const teacherHash = { - type: "teacher", - orderBy: "registrationDate", - size: 25, -}; - -export default function CorporateDashboard({user, linkedCorporate}: Props) { - const [selectedUser, setSelectedUser] = useState(); - const [showModal, setShowModal] = useState(false); - - const {data: stats} = useFilterRecordsByUser(); - const {groups} = useGroups({admin: user.id}); - const {assignments, isLoading: isAssignmentsLoading, reload: reloadAssignments} = useAssignments({corporate: user.id}); - const {balance} = useUserBalance(); - - const {users: students, total: totalStudents, reload: reloadStudents, isLoading: isStudentsLoading} = useUsers(studentHash); - const {users: teachers, total: totalTeachers, reload: reloadTeachers, isLoading: isTeachersLoading} = useUsers(teacherHash); - - const appendUserFilters = useFilterStore((state) => state.appendUserFilter); - const router = useRouter(); - - const assignmentsGroups = useMemo(() => groups.filter((x) => x.admin === user.id || x.participants.includes(user.id)), [groups, user.id]); - - useEffect(() => { - setShowModal(!!selectedUser && router.asPath === "/#"); - }, [selectedUser, router.asPath]); - - 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 GroupsList = () => { - const filter = (x: Group) => x.admin === user.id || x.participants.includes(user.id); - - return ( - <> -
-
router.push("/")} - 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: students.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)); - - return calculateAverageLevel(levels); - }; - - if (router.asPath === "/#students") - return ( - ( -
-
router.push("/")} - 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})

-
- )} - /> - ); - - if (router.asPath === "/#teachers") - return ( - ( -
-
router.push("/")} - 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})

-
- )} - /> - ); - - if (router.asPath === "/#groups") return ; - if (router.asPath === "/#studentsPerformance") return ; - - if (router.asPath === "/#assignments") - return ( - router.push("/")} - /> - ); - - if (router.asPath === "/#statistical") return ; - - return ( - <> - setSelectedUser(undefined)}> - <> - {selectedUser && ( -
- { - setSelectedUser(undefined); - if (shouldReload && selectedUser!.type === "student") reloadStudents(); - if (shouldReload && selectedUser!.type === "teacher") reloadTeachers(); - }} - 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("/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("/users"); - } - : undefined - } - user={selectedUser} - /> -
- )} - -
- - <> - {!!linkedCorporate && ( -
- Linked to: {linkedCorporate?.corporateInformation?.companyInformation.name || linkedCorporate.name} -
- )} -
- router.push("/#students")} - isLoading={isStudentsLoading} - Icon={BsPersonFill} - label="Students" - value={totalStudents} - color="purple" - /> - router.push("/#teachers")} - isLoading={isTeachersLoading} - Icon={BsPencilSquare} - label="Teachers" - value={totalTeachers} - 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" - /> - router.push("/#groups")} Icon={BsPeople} label="Groups" value={groups.length} color="purple" /> - - - router.push("/#studentsPerformance")} - /> - router.push("/#statistical")} /> - -
- -
-
- Latest students -
- {students - .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) - .map((x) => ( - - ))} -
-
-
- Latest teachers -
- {teachers - .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) - .map((x) => ( - - ))} -
-
-
- Highest level students -
- {students - .sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels)) - .map((x) => ( - - ))} -
-
-
- Highest exam count students -
- {students - .sort( - (a, b) => - Object.keys(groupByExam(getStatsByStudent(b))).length - Object.keys(groupByExam(getStatsByStudent(a))).length, - ) - .map((x) => ( - - ))} -
-
-
- - - ); -} diff --git a/src/dashboards/CorporateStudentsLevels.tsx b/src/dashboards/CorporateStudentsLevels.tsx deleted file mode 100644 index 203b3caf..00000000 --- a/src/dashboards/CorporateStudentsLevels.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, {useMemo} from "react"; -import useUsers, { userHashStudent, userHashTeacher, userHashCorporate } 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 [corporateId, setCorporateId] = React.useState(""); - - const {users: students} = useUsers(userHashStudent); - const {users: corporates} = useUsers(userHashCorporate); - - const corporate = useMemo(() => corporates.find((u) => u.id === corporateId) || corporates[0], [corporates, corporateId]); - - return ( - <> - ({ - value: x?.id || "N/A", - label: x?.corporateInformation?.companyInformation?.name || x?.name || "N/A", - }))} - isClearable - value={ - selectedCorporate === null - ? null - : { - value: selectedCorporate?.id || "N/A", - label: - selectedCorporate?.corporateInformation?.companyInformation?.name || - selectedCorporate?.name || - "N/A", - } - } - placeholder="Select a Corporate..." - onChange={(value) => - !value - ? setSelectedCorporate(null) - : setSelectedCorporate( - value.value === "N/A" ? undefined : availableCorporates.find((x) => x?.id === value.value), - ) - } - /> - setName(e)} - placeholder="Enter your name" - defaultValue={name} - required - /> - setEmail(e.toLowerCase())} - placeholder="Enter email address" - defaultValue={email} - required - /> -
+ return ( +
+
+ setName(e)} + placeholder="Enter your name" + defaultValue={name} + required + /> + setEmail(e.toLowerCase())} + placeholder="Enter email address" + defaultValue={email} + required + /> +
-
- setPassword(e)} - placeholder="Enter your password" - defaultValue={password} - required - /> - setConfirmPassword(e)} - placeholder="Confirm your password" - defaultValue={confirmPassword} - required - /> -
+
+ setPassword(e)} + placeholder="Enter your password" + defaultValue={password} + required + /> + setConfirmPassword(e)} + placeholder="Confirm your password" + defaultValue={confirmPassword} + required + /> +
- + -
- setCompanyName(e)} - placeholder="Corporate name" - label="Corporate name" - defaultValue={companyName} - required - /> - setCompanyUsers(parseInt(e))} - label="Number of users" - defaultValue={companyUsers} - required - /> -
+
+ setCompanyName(e)} + placeholder="Corporate name" + label="Corporate name" + defaultValue={companyName} + required + /> + setCompanyUsers(parseInt(e))} + label="Number of users" + defaultValue={companyUsers} + required + /> +
-
-
- - u.type === "agent") + .map((x) => ({ value: x.id, label: `${x.name} - ${x.email}` })), + ]} + defaultValue={{ value: "", label: "No referral" }} + onChange={(value) => setReferralAgent(value?.value)} + styles={{ + control: (styles) => ({ + ...styles, + paddingLeft: "4px", + border: "none", + outline: "none", + ":focus": { + outline: "none", + }, + }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isFocused + ? "#D5D9F0" + : state.isSelected + ? "#7872BF" + : "white", + color: state.isFocused ? "black" : styles.color, + }), + }} + /> +
-
- - ({ + value, + label: + availableDurations[value as keyof typeof availableDurations] + .label, + }))} + defaultValue={{ + value: "1_month", + label: availableDurations["1_month"].label, + }} + onChange={(value) => + setSubscriptionDuration( + value + ? availableDurations[ + value.value as keyof typeof availableDurations + ].number + : 1, + ) + } + styles={{ + control: (styles) => ({ + ...styles, + paddingLeft: "4px", + border: "none", + outline: "none", + ":focus": { + outline: "none", + }, + }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isFocused + ? "#D5D9F0" + : state.isSelected + ? "#7872BF" + : "white", + color: state.isFocused ? "black" : styles.color, + }), + }} + /> +
+
+
+ {renderCheckbox()} +
+ + + ); } diff --git a/src/pages/(status)/PaymentDue.tsx b/src/pages/(status)/PaymentDue.tsx index c8d90612..a85d52c9 100644 --- a/src/pages/(status)/PaymentDue.tsx +++ b/src/pages/(status)/PaymentDue.tsx @@ -3,15 +3,15 @@ import Layout from "@/components/High/Layout"; import useGroups from "@/hooks/useGroups"; import usePackages from "@/hooks/usePackages"; import useUsers from "@/hooks/useUsers"; -import {User} from "@/interfaces/user"; +import { User } from "@/interfaces/user"; import clsx from "clsx"; -import {capitalize} from "lodash"; -import {useEffect, useState} from "react"; +import { capitalize } from "lodash"; +import { useEffect, useState } from "react"; import useInvites from "@/hooks/useInvites"; -import {BsArrowRepeat} from "react-icons/bs"; +import { BsArrowRepeat } from "react-icons/bs"; import InviteCard from "@/components/Medium/InviteCard"; -import {useRouter} from "next/router"; -import {ToastContainer} from "react-toastify"; +import { useRouter } from "next/router"; +import { ToastContainer } from "react-toastify"; import useDiscounts from "@/hooks/useDiscounts"; import PaymobPayment from "@/components/PaymobPayment"; import moment from "moment"; @@ -22,17 +22,17 @@ interface Props { reload: () => void; } -export default function PaymentDue({user, hasExpired = false, reload}: Props) { +export default function PaymentDue({ user, hasExpired = false, reload }: Props) { const [isLoading, setIsLoading] = useState(false); const [appliedDiscount, setAppliedDiscount] = useState(0); const router = useRouter(); - const {packages} = usePackages(); - const {discounts} = useDiscounts(); - const {users} = useUsers(); - const {groups} = useGroups({}); - const {invites, isLoading: isInvitesLoading, reload: reloadInvites} = useInvites({to: user?.id}); + const { packages } = usePackages(); + const { discounts } = useDiscounts(); + const { users } = useUsers(); + const { groups } = useGroups({}); + const { invites, isLoading: isInvitesLoading, reload: reloadInvites } = useInvites({ to: user?.id }); useEffect(() => { const userDiscounts = discounts.filter((x) => user.email.endsWith(`@${x.domain}`)); @@ -172,7 +172,7 @@ export default function PaymentDue({user, hasExpired = false, reload}: Props) {
EnCoach's Logo - EnCoach - {user.corporateInformation?.monthlyDuration} Months + EnCoach - {12} Months
@@ -184,7 +184,7 @@ export default function PaymentDue({user, hasExpired = false, reload}: Props) { setIsPaymentLoading={setIsLoading} currency={user.corporateInformation.payment.currency} price={user.corporateInformation.payment.value} - duration={user.corporateInformation.monthlyDuration} + duration={12} duration_unit="months" onSuccess={() => { setIsLoading(false); @@ -196,8 +196,7 @@ export default function PaymentDue({user, hasExpired = false, reload}: Props) { This includes:
  • - - Allow a total of {user.corporateInformation.companyInformation.userAmount} students and teachers - to use EnCoach + - Allow a total of 0 students and teachers to use EnCoach
  • - Train their abilities for the IELTS exam
  • - Gain insights into your students' weaknesses and strengths
  • diff --git a/src/pages/api/assignments/[id]/[export]/excel.ts b/src/pages/api/assignments/[id]/[export]/excel.ts index a3e57b1d..3e8a6ac1 100644 --- a/src/pages/api/assignments/[id]/[export]/excel.ts +++ b/src/pages/api/assignments/[id]/[export]/excel.ts @@ -13,23 +13,23 @@ import { getStudentGroupsForUsersWithoutAdmin } from "@/utils/groups.be"; import { getSpecificUsers, getUser } from "@/utils/users.be"; import { getUserName } from "@/utils/users"; interface GroupScoreSummaryHelper { - score: [number, number]; - label: string; - sessions: string[]; + score: [number, number]; + label: string; + sessions: string[]; } interface AssignmentData { - id: string; - assigner: string; - assignees: string[]; - results: any; - exams: { module: Module }[]; - startDate: string; - excel: { - path: string; - version: string; - }; - name: string; + id: string; + assigner: string; + assignees: string[]; + results: any; + exams: { module: Module }[]; + startDate: string; + excel: { + path: string; + version: string; + }; + name: string; } const db = client.db(process.env.MONGODB_DB); @@ -37,427 +37,427 @@ const db = client.db(process.env.MONGODB_DB); export default withIronSessionApiRoute(handler, sessionOptions); async function handler(req: NextApiRequest, res: NextApiResponse) { - // if (req.method === "GET") return get(req, res); - if (req.method === "POST") return await post(req, res); + // if (req.method === "GET") return get(req, res); + if (req.method === "POST") return await post(req, res); } function logWorksheetData(worksheet: any) { - worksheet.eachRow((row: any, rowNumber: number) => { - console.log(`Row ${rowNumber}:`); - row.eachCell((cell: any, colNumber: number) => { - console.log(` Cell ${colNumber}: ${cell.value}`); - }); - }); + worksheet.eachRow((row: any, rowNumber: number) => { + console.log(`Row ${rowNumber}:`); + row.eachCell((cell: any, colNumber: number) => { + console.log(` Cell ${colNumber}: ${cell.value}`); + }); + }); } function commonExcel({ - data, - userName, - users, - sectionName, - customTable, - customTableHeaders, - renderCustomTableData, + data, + userName, + users, + sectionName, + customTable, + customTableHeaders, + renderCustomTableData, }: { - data: AssignmentData; - userName: string; - users: User[]; - sectionName: string; - customTable: string[][]; - customTableHeaders: string[]; - renderCustomTableData: (data: any) => string[]; + data: AssignmentData; + userName: string; + users: User[]; + sectionName: string; + customTable: string[][]; + customTableHeaders: string[]; + renderCustomTableData: (data: any) => string[]; }) { - const allStats = data.results.flatMap((r: any) => r.stats); + const allStats = data.results.flatMap((r: any) => r.stats); - const uniqueExercises = [...new Set(allStats.map((s: any) => s.exercise))]; + const uniqueExercises = [...new Set(allStats.map((s: any) => s.exercise))]; - const assigneesData = data.assignees - .map((assignee: string) => { - const userStats = allStats.filter((s: any) => s.user === assignee); - const dates = userStats.map((s: any) => moment(s.date)); - const user = users.find((u) => u.id === assignee); - return { - userId: assignee, - // added some default values in case the user is not found - // could it be possible to have an assigned user deleted from the database? - user: user || { - name: "Unknown", - email: "Unknown", - demographicInformation: { passportId: "Unknown", gender: "Unknown" }, - }, - ...userStats.reduce( - (acc: any, curr: any) => { - return { - ...acc, - correct: acc.correct + curr.score.correct, - missing: acc.missing + curr.score.missing, - total: acc.total + curr.score.total, - }; - }, - { correct: 0, missing: 0, total: 0 } - ), - firstDate: moment.min(...dates), - lastDate: moment.max(...dates), - stats: userStats, - }; - }) - .sort((a, b) => b.correct - a.correct); + const assigneesData = data.assignees + .map((assignee: string) => { + const userStats = allStats.filter((s: any) => s.user === assignee); + const dates = userStats.map((s: any) => moment(s.date)); + const user = users.find((u) => u.id === assignee); + return { + userId: assignee, + // added some default values in case the user is not found + // could it be possible to have an assigned user deleted from the database? + user: user || { + name: "Unknown", + email: "Unknown", + demographicInformation: { passportId: "Unknown", gender: "Unknown" }, + }, + ...userStats.reduce( + (acc: any, curr: any) => { + return { + ...acc, + correct: acc.correct + curr.score.correct, + missing: acc.missing + curr.score.missing, + total: acc.total + curr.score.total, + }; + }, + { correct: 0, missing: 0, total: 0 } + ), + firstDate: moment.min(...dates), + lastDate: moment.max(...dates), + stats: userStats, + }; + }) + .sort((a, b) => b.correct - a.correct); - const results = assigneesData.map((r: any) => r.correct); - const highestScore = Math.max(...results); - const lowestScore = Math.min(...results); - const averageScore = results.reduce((a, b) => a + b, 0) / results.length; - const firstDate = moment.min(assigneesData.map((r: any) => r.firstDate)); - const lastDate = moment.max(assigneesData.map((r: any) => r.lastDate)); + const results = assigneesData.map((r: any) => r.correct); + const highestScore = Math.max(...results); + const lowestScore = Math.min(...results); + const averageScore = results.reduce((a, b) => a + b, 0) / results.length; + const firstDate = moment.min(assigneesData.map((r: any) => r.firstDate)); + const lastDate = moment.max(assigneesData.map((r: any) => r.lastDate)); - const firstSectionData = [ - { - label: sectionName, - value: userName, - }, - { - label: "Report Download date :", - value: moment().format("DD/MM/YYYY"), - }, - { label: "Test Information :", value: data.name }, - { - label: "Date of Test :", - value: moment(data.startDate).format("DD/MM/YYYY"), - }, - { label: "Number of Candidates :", value: data.assignees.length }, - { label: "Highest score :", value: highestScore }, - { label: "Lowest score :", value: lowestScore }, - { label: "Average score :", value: averageScore }, - { label: "", value: "" }, - { - label: "Date and time of First submission :", - value: firstDate.format("DD/MM/YYYY"), - }, - { - label: "Date and time of Last submission :", - value: lastDate.format("DD/MM/YYYY"), - }, - ]; + const firstSectionData = [ + { + label: sectionName, + value: userName, + }, + { + label: "Report Download date :", + value: moment().format("DD/MM/YYYY"), + }, + { label: "Test Information :", value: data.name }, + { + label: "Date of Test :", + value: moment(data.startDate).format("DD/MM/YYYY"), + }, + { label: "Number of Candidates :", value: data.assignees.length }, + { label: "Highest score :", value: highestScore }, + { label: "Lowest score :", value: lowestScore }, + { label: "Average score :", value: averageScore }, + { label: "", value: "" }, + { + label: "Date and time of First submission :", + value: firstDate.format("DD/MM/YYYY"), + }, + { + label: "Date and time of Last submission :", + value: lastDate.format("DD/MM/YYYY"), + }, + ]; - // Create a new workbook and add a worksheet - const workbook = new ExcelJS.Workbook(); - const worksheet = workbook.addWorksheet("Report Data"); + // Create a new workbook and add a worksheet + const workbook = new ExcelJS.Workbook(); + const worksheet = workbook.addWorksheet("Report Data"); - // Populate the worksheet with the data - firstSectionData.forEach(({ label, value }, index) => { - worksheet.getCell(`A${index + 1}`).value = label; // First column (labels) - worksheet.getCell(`B${index + 1}`).value = value; // Second column (values) - }); + // Populate the worksheet with the data + firstSectionData.forEach(({ label, value }, index) => { + worksheet.getCell(`A${index + 1}`).value = label; // First column (labels) + worksheet.getCell(`B${index + 1}`).value = value; // Second column (values) + }); - // added empty arrays to force row spacings - const customTableAndLine = [[], ...customTable, []]; - customTableAndLine.forEach((row: string[], index) => { - worksheet.addRow(row); - }); + // added empty arrays to force row spacings + const customTableAndLine = [[], ...customTable, []]; + customTableAndLine.forEach((row: string[], index) => { + worksheet.addRow(row); + }); - // Define the static part of the headers (before "Test Sections") - const staticHeaders = [ - "Sr N", - "Candidate ID", - "First and Last Name", - "Passport/ID", - "Email ID", - "Gender", - ...customTableHeaders, - ]; + // Define the static part of the headers (before "Test Sections") + const staticHeaders = [ + "Sr N", + "Candidate ID", + "First and Last Name", + "Passport/ID", + "Email ID", + "Gender", + ...customTableHeaders, + ]; - // Define additional headers after "Test Sections" - const additionalHeaders = ["Time Spent", "Date", "Score"]; + // Define additional headers after "Test Sections" + const additionalHeaders = ["Time Spent", "Date", "Score"]; - // Calculate the dynamic columns based on the testSectionsArray - const testSectionHeaders = uniqueExercises.map( - (section, index) => `Part ${index + 1}` - ); + // Calculate the dynamic columns based on the testSectionsArray + const testSectionHeaders = uniqueExercises.map( + (section, index) => `Part ${index + 1}` + ); - const tableColumnHeadersFirstPart = [ - ...staticHeaders, - ...uniqueExercises.map((a) => "Test Sections"), - ]; - // Add the main header row, merging static columns and "Test Sections" - const tableColumnHeaders = [ - ...tableColumnHeadersFirstPart, - ...additionalHeaders, - ]; - worksheet.addRow(tableColumnHeaders); + const tableColumnHeadersFirstPart = [ + ...staticHeaders, + ...uniqueExercises.map((a) => "Test Sections"), + ]; + // Add the main header row, merging static columns and "Test Sections" + const tableColumnHeaders = [ + ...tableColumnHeadersFirstPart, + ...additionalHeaders, + ]; + worksheet.addRow(tableColumnHeaders); - // 1 headers rows - const startIndexTable = - firstSectionData.length + customTableAndLine.length + 1; + // 1 headers rows + const startIndexTable = + firstSectionData.length + customTableAndLine.length + 1; - // // Merge "Test Sections" over dynamic number of columns - // const tableColumns = staticHeaders.length + numberOfTestSections; + // // Merge "Test Sections" over dynamic number of columns + // const tableColumns = staticHeaders.length + numberOfTestSections; - // K10:M12 = 10,11,12,13 - // horizontally group Test Sections + // K10:M12 = 10,11,12,13 + // horizontally group Test Sections - // if there are test section headers to even merge: - if (testSectionHeaders.length > 1) { - worksheet.mergeCells( - startIndexTable, - staticHeaders.length + 1, - startIndexTable, - tableColumnHeadersFirstPart.length - ); - } + // if there are test section headers to even merge: + if (testSectionHeaders.length > 1) { + worksheet.mergeCells( + startIndexTable, + staticHeaders.length + 1, + startIndexTable, + tableColumnHeadersFirstPart.length + ); + } - // Add the dynamic second and third header rows for test sections and sub-columns - worksheet.addRow([ - ...Array(staticHeaders.length).fill(""), - ...testSectionHeaders, - "", - "", - "", - ]); - worksheet.addRow([ - ...Array(staticHeaders.length).fill(""), - ...uniqueExercises.map(() => "Grammar & Vocabulary"), - "", - "", - "", - ]); - worksheet.addRow([ - ...Array(staticHeaders.length).fill(""), - ...uniqueExercises.map( - (exercise) => allStats.find((s: any) => s.exercise === exercise).type - ), - "", - "", - "", - ]); + // Add the dynamic second and third header rows for test sections and sub-columns + worksheet.addRow([ + ...Array(staticHeaders.length).fill(""), + ...testSectionHeaders, + "", + "", + "", + ]); + worksheet.addRow([ + ...Array(staticHeaders.length).fill(""), + ...uniqueExercises.map(() => "Grammar & Vocabulary"), + "", + "", + "", + ]); + worksheet.addRow([ + ...Array(staticHeaders.length).fill(""), + ...uniqueExercises.map( + (exercise) => allStats.find((s: any) => s.exercise === exercise).type + ), + "", + "", + "", + ]); - // vertically group based on the part, exercise and type - staticHeaders.forEach((header, index) => { - worksheet.mergeCells( - startIndexTable, - index + 1, - startIndexTable + 3, - index + 1 - ); - }); + // vertically group based on the part, exercise and type + staticHeaders.forEach((header, index) => { + worksheet.mergeCells( + startIndexTable, + index + 1, + startIndexTable + 3, + index + 1 + ); + }); - assigneesData.forEach((data, index) => { - worksheet.addRow([ - index + 1, - data.userId, - data.user.name, - data.user.demographicInformation?.passportId, - data.user.email, - data.user.demographicInformation?.gender, - ...renderCustomTableData(data), - ...uniqueExercises.map((exercise) => { - const score = data.stats.find( - (s: any) => s.exercise === exercise && s.user === data.userId - ).score; - return `${score.correct}/${score.total}`; - }), - `${Math.ceil( - data.stats.reduce((acc: number, curr: any) => acc + curr.timeSpent, 0) / - 60 - )} minutes`, - data.lastDate.format("DD/MM/YYYY HH:mm"), - data.correct, - ]); - }); + assigneesData.forEach((data, index) => { + worksheet.addRow([ + index + 1, + data.userId, + data.user.name, + data.user.demographicInformation?.passportId, + data.user.email, + data.user.demographicInformation?.gender, + ...renderCustomTableData(data), + ...uniqueExercises.map((exercise) => { + const score = data.stats.find( + (s: any) => s.exercise === exercise && s.user === data.userId + ).score; + return `${score.correct}/${score.total}`; + }), + `${Math.ceil( + data.stats.reduce((acc: number, curr: any) => acc + curr.timeSpent, 0) / + 60 + )} minutes`, + data.lastDate.format("DD/MM/YYYY HH:mm"), + data.correct, + ]); + }); - worksheet.addRow([""]); - worksheet.addRow([""]); + worksheet.addRow([""]); + worksheet.addRow([""]); - for (let i = 0; i < tableColumnHeaders.length; i++) { - worksheet.getColumn(i + 1).width = 30; - } + for (let i = 0; i < tableColumnHeaders.length; i++) { + worksheet.getColumn(i + 1).width = 30; + } - // Apply styles to the headers - [startIndexTable].forEach((rowNumber) => { - worksheet.getRow(rowNumber).eachCell((cell) => { - if (cell.value) { - cell.fill = { - type: "pattern", - pattern: "solid", - fgColor: { argb: "FFBFBFBF" }, // Grey color for headers - }; - cell.font = { bold: true }; - cell.alignment = { vertical: "middle", horizontal: "center" }; - } - }); - }); + // Apply styles to the headers + [startIndexTable].forEach((rowNumber) => { + worksheet.getRow(rowNumber).eachCell((cell) => { + if (cell.value) { + cell.fill = { + type: "pattern", + pattern: "solid", + fgColor: { argb: "FFBFBFBF" }, // Grey color for headers + }; + cell.font = { bold: true }; + cell.alignment = { vertical: "middle", horizontal: "center" }; + } + }); + }); - worksheet.addRow(["Printed by: Confidential Information"]); - worksheet.addRow(["info@encoach.com"]); + worksheet.addRow(["Printed by: Confidential Information"]); + worksheet.addRow(["info@encoach.com"]); - // Convert workbook to Buffer (Node.js) or Blob (Browser) - return workbook.xlsx.writeBuffer(); + // Convert workbook to Buffer (Node.js) or Blob (Browser) + return workbook.xlsx.writeBuffer(); } function corporateAssignment( - user: CorporateUser, - data: AssignmentData, - users: User[] + user: CorporateUser, + data: AssignmentData, + users: User[] ) { - return commonExcel({ - data, - userName: user.corporateInformation?.companyInformation?.name || "", - users, - sectionName: "Corporate Name :", - customTable: [], - customTableHeaders: [], - renderCustomTableData: () => [], - }); + return commonExcel({ + data, + userName: user.name || "", + users, + sectionName: "Corporate Name :", + customTable: [], + customTableHeaders: [], + renderCustomTableData: () => [], + }); } async function mastercorporateAssignment( - user: MasterCorporateUser, - data: AssignmentData, - users: User[] + user: MasterCorporateUser, + data: AssignmentData, + users: User[] ) { - const userGroups = await getStudentGroupsForUsersWithoutAdmin( - user.id, - data.assignees - ); - const adminUsers = [...new Set(userGroups.map((g) => g.admin))]; + const userGroups = await getStudentGroupsForUsersWithoutAdmin( + user.id, + data.assignees + ); + const adminUsers = [...new Set(userGroups.map((g) => g.admin))]; - const userGroupsParticipants = userGroups.flatMap((g) => g.participants); - const adminsData = await getSpecificUsers(adminUsers); - const companiesData = adminsData.map((user) => { - const name = getUserName(user); - const users = userGroupsParticipants.filter((p) => - data.assignees.includes(p) - ); + const userGroupsParticipants = userGroups.flatMap((g) => g.participants); + const adminsData = await getSpecificUsers(adminUsers); + const companiesData = adminsData.map((user) => { + const name = getUserName(user); + const users = userGroupsParticipants.filter((p) => + data.assignees.includes(p) + ); - const stats = data.results - .flatMap((r: any) => r.stats) - .filter((s: any) => users.includes(s.user)); - const correct = stats.reduce( - (acc: number, s: any) => acc + s.score.correct, - 0 - ); - const total = stats.reduce( - (acc: number, curr: any) => acc + curr.score.total, - 0 - ); + const stats = data.results + .flatMap((r: any) => r.stats) + .filter((s: any) => users.includes(s.user)); + const correct = stats.reduce( + (acc: number, s: any) => acc + s.score.correct, + 0 + ); + const total = stats.reduce( + (acc: number, curr: any) => acc + curr.score.total, + 0 + ); - return { - name, - correct, - total, - }; - }); + return { + name, + correct, + total, + }; + }); - const customTable = [ - ...companiesData, - { - name: "Total", - correct: companiesData.reduce((acc, curr) => acc + curr.correct, 0), - total: companiesData.reduce((acc, curr) => acc + curr.total, 0), - }, - ].map((c) => [c.name, `${c.correct}/${c.total}`]); + const customTable = [ + ...companiesData, + { + name: "Total", + correct: companiesData.reduce((acc, curr) => acc + curr.correct, 0), + total: companiesData.reduce((acc, curr) => acc + curr.total, 0), + }, + ].map((c) => [c.name, `${c.correct}/${c.total}`]); - const customTableHeaders = [ - { name: "Corporate", helper: (data: any) => data.user.corporateName }, - ]; - return commonExcel({ - data, - userName: user.corporateInformation?.companyInformation?.name || "", - users: users.map((u) => { - const userGroup = userGroups.find((g) => g.participants.includes(u.id)); - const admin = adminsData.find((a) => a.id === userGroup?.admin); - return { - ...u, - corporateName: getUserName(admin), - }; - }), - sectionName: "Master Corporate Name :", - customTable: [["Corporate Summary"], ...customTable], - customTableHeaders: customTableHeaders.map((h) => h.name), - renderCustomTableData: (data) => - customTableHeaders.map((h) => h.helper(data)), - }); + const customTableHeaders = [ + { name: "Corporate", helper: (data: any) => data.user.corporateName }, + ]; + return commonExcel({ + data, + userName: user.name || "", + users: users.map((u) => { + const userGroup = userGroups.find((g) => g.participants.includes(u.id)); + const admin = adminsData.find((a) => a.id === userGroup?.admin); + return { + ...u, + corporateName: getUserName(admin), + }; + }), + sectionName: "Master Corporate Name :", + customTable: [["Corporate Summary"], ...customTable], + customTableHeaders: customTableHeaders.map((h) => h.name), + renderCustomTableData: (data) => + customTableHeaders.map((h) => h.helper(data)), + }); } async function post(req: NextApiRequest, res: NextApiResponse) { - // verify if it's a logged user that is trying to export - if (req.session.user) { - const { id } = req.query as { id: string }; + // verify if it's a logged user that is trying to export + if (req.session.user) { + const { id } = req.query as { id: string }; - const assignment = await db.collection("assignments").findOne({ id: id }); - if (!assignment) { - res.status(400).end(); - return; - } + const assignment = await db.collection("assignments").findOne({ id: id }); + if (!assignment) { + res.status(400).end(); + return; + } - // if ( - // data.excel && - // data.excel.path && - // data.excel.version === process.env.EXCEL_VERSION - // ) { - // // if it does, return the excel url - // const fileRef = ref(storage, data.excel.path); - // const url = await getDownloadURL(fileRef); - // res.status(200).end(url); - // return; - // } + // if ( + // data.excel && + // data.excel.path && + // data.excel.version === process.env.EXCEL_VERSION + // ) { + // // if it does, return the excel url + // const fileRef = ref(storage, data.excel.path); + // const url = await getDownloadURL(fileRef); + // res.status(200).end(url); + // return; + // } - const objectIds = assignment.assignees.map(id => id); + const objectIds = assignment.assignees.map(id => id); - const users = await db.collection("users").find({ - id: { $in: objectIds } - }).toArray(); + const users = await db.collection("users").find({ + id: { $in: objectIds } + }).toArray(); - const user = await db.collection("users").findOne({ id: assignment.assigner }); + const user = await db.collection("users").findOne({ id: assignment.assigner }); - // we'll need the user in order to get the user data (name, email, focus, etc); - if (user && users) { - // generate the file ref for storage - const fileName = `${Date.now().toString()}.xlsx`; - const refName = `assignment_report/${fileName}`; - const fileRef = ref(storage, refName); + // we'll need the user in order to get the user data (name, email, focus, etc); + if (user && users) { + // generate the file ref for storage + const fileName = `${Date.now().toString()}.xlsx`; + const refName = `assignment_report/${fileName}`; + const fileRef = ref(storage, refName); - const getExcelFn = () => { - switch (user.type) { - case "teacher": - case "corporate": - return corporateAssignment(user as CorporateUser, assignment, users); - case "mastercorporate": - return mastercorporateAssignment( - user as MasterCorporateUser, - assignment, - users - ); - default: - throw new Error("Invalid user type"); - } - }; - const buffer = await getExcelFn(); + const getExcelFn = () => { + switch (user.type) { + case "teacher": + case "corporate": + return corporateAssignment(user as CorporateUser, assignment, users); + case "mastercorporate": + return mastercorporateAssignment( + user as MasterCorporateUser, + assignment, + users + ); + default: + throw new Error("Invalid user type"); + } + }; + const buffer = await getExcelFn(); - // upload the pdf to storage - await uploadBytes(fileRef, buffer, { - contentType: - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - }); + // upload the pdf to storage + await uploadBytes(fileRef, buffer, { + contentType: + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); - // update the stats entries with the pdf url to prevent duplication - await db.collection("assignments").updateOne( - { id: assignment.id }, - { - $set: { - excel: { - path: refName, - version: process.env.EXCEL_VERSION, - } - } - } - ); + // update the stats entries with the pdf url to prevent duplication + await db.collection("assignments").updateOne( + { id: assignment.id }, + { + $set: { + excel: { + path: refName, + version: process.env.EXCEL_VERSION, + } + } + } + ); - const url = await getDownloadURL(fileRef); - res.status(200).end(url); + const url = await getDownloadURL(fileRef); + res.status(200).end(url); - return; - } - } + return; + } + } - res.status(401).json({ message: "Unauthorized" }); + res.status(401).json({ message: "Unauthorized" }); } diff --git a/src/pages/api/assignments/[id]/[export]/pdf.tsx b/src/pages/api/assignments/[id]/[export]/pdf.tsx index afbe08c6..32d90dc3 100644 --- a/src/pages/api/assignments/[id]/[export]/pdf.tsx +++ b/src/pages/api/assignments/[id]/[export]/pdf.tsx @@ -310,19 +310,17 @@ async function post(req: NextApiRequest, res: NextApiResponse) { if (groups.length > 0) { const admins = await db.collection("users") - .find({ id: { $in: groups.map(g => g.admin).map(id => id)} }) + .find({ id: { $in: groups.map(g => g.admin).map(id => id) } }) .toArray(); - const adminData = admins.find((a) => a.corporateInformation?.companyInformation?.name); + const adminData = admins.find((a) => a.name); if (adminData) { - return adminData.corporateInformation.companyInformation.name; + return adminData.name; } } } - if (assignerUser.type === "corporate" && assignerUser.corporateInformation?.companyInformation?.name) { - return assignerUser.corporateInformation.companyInformation.name; - } + return assignerUser.type } } catch (err) { console.error(err); @@ -372,16 +370,16 @@ async function post(req: NextApiRequest, res: NextApiResponse) { // update the stats entries with the pdf url to prevent duplication await db.collection("assignments").updateOne( { id: data.id }, - { - $set: { - pdf: { - path: refName, - version: process.env.PDF_VERSION, + { + $set: { + pdf: { + path: refName, + version: process.env.PDF_VERSION, + } } - } } - ); - + ); + const url = await getDownloadURL(fileRef); res.status(200).end(url); return; diff --git a/src/pages/api/code/index.ts b/src/pages/api/code/index.ts index e290dd6d..f3d7410f 100644 --- a/src/pages/api/code/index.ts +++ b/src/pages/api/code/index.ts @@ -62,7 +62,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) { if (req.session.user.type === "corporate") { const totalCodes = userCodes.filter((x) => !x.userId || !usersInGroups.includes(x.userId)).length + usersInGroups.length + codes.length; - const allowedCodes = req.session.user.corporateInformation?.companyInformation.userAmount || 0; + const allowedCodes = 0; if (totalCodes > allowedCodes) { res.status(403).json({ @@ -127,7 +127,7 @@ async function post(req: NextApiRequest, res: NextApiResponse) { // upsert: true -> if it doesnt exist insert await db.collection("codes").updateOne( { id: code }, - { $set: { id: code, ...codeInformation} }, + { $set: { id: code, ...codeInformation } }, { upsert: true } ); } diff --git a/src/pages/api/make_user.ts b/src/pages/api/make_user.ts index bd05953a..6e9b0269 100644 --- a/src/pages/api/make_user.ts +++ b/src/pages/api/make_user.ts @@ -14,17 +14,17 @@ import { getEntityWithRoles } from "@/utils/entities.be"; import { findBy } from "@/utils"; const DEFAULT_DESIRED_LEVELS = { - reading: 9, - listening: 9, - writing: 9, - speaking: 9, + reading: 9, + listening: 9, + writing: 9, + speaking: 9, }; const DEFAULT_LEVELS = { - reading: 0, - listening: 0, - writing: 0, - speaking: 0, + reading: 0, + listening: 0, + writing: 0, + speaking: 0, }; const auth = getAuth(app); @@ -33,99 +33,94 @@ const db = client.db(process.env.MONGODB_DB); export default withIronSessionApiRoute(handler, sessionOptions); async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method === "POST") return post(req, res); + if (req.method === "POST") return post(req, res); - return res.status(404).json({ ok: false }); + return res.status(404).json({ ok: false }); } async function post(req: NextApiRequest, res: NextApiResponse) { - const maker = req.session.user; - if (!maker) { - return res.status(401).json({ ok: false, reason: "You must be logged in to make user!" }); - } + const maker = req.session.user; + if (!maker) { + return res.status(401).json({ ok: false, reason: "You must be logged in to make user!" }); + } - const { email, passport_id, password, type, groupID, entity, expiryDate, corporate } = req.body as { - email: string; - password?: string; - passport_id: string; - type: string; - entity: string; - groupID?: string; - corporate?: string; - expiryDate: null | Date; - }; + const { email, passport_id, password, type, groupID, entity, expiryDate, corporate } = req.body as { + email: string; + password?: string; + passport_id: string; + type: string; + entity: string; + groupID?: string; + corporate?: string; + expiryDate: null | Date; + }; - // cleaning data - delete req.body.passport_id; - delete req.body.groupID; - delete req.body.expiryDate; - delete req.body.password; - delete req.body.corporate; - delete req.body.entity + // cleaning data + delete req.body.passport_id; + delete req.body.groupID; + delete req.body.expiryDate; + delete req.body.password; + delete req.body.corporate; + delete req.body.entity - await createUserWithEmailAndPassword(auth, email.toLowerCase(), !!password ? password : passport_id) - .then(async (userCredentials) => { - const userId = userCredentials.user.uid; + await createUserWithEmailAndPassword(auth, email.toLowerCase(), !!password ? password : passport_id) + .then(async (userCredentials) => { + const userId = userCredentials.user.uid; - const entityWithRole = await getEntityWithRoles(entity) - const defaultRole = findBy(entityWithRole?.roles || [], "isDefault", true) + const entityWithRole = await getEntityWithRoles(entity) + const defaultRole = findBy(entityWithRole?.roles || [], "isDefault", true) - const user = { - ...req.body, - bio: "", - id: userId, - type: type, - focus: "academic", - status: "active", - desiredLevels: DEFAULT_DESIRED_LEVELS, - profilePicture: "/defaultAvatar.png", - levels: DEFAULT_LEVELS, - isFirstLogin: false, - isVerified: true, - registrationDate: new Date(), - entities: [{ id: entity, role: defaultRole?.id || "" }], - subscriptionExpirationDate: expiryDate || null, - ...((maker.type === "corporate" || maker.type === "mastercorporate") && type === "corporate" - ? { - corporateInformation: { - companyInformation: { - name: maker.corporateInformation?.companyInformation?.name || "N/A", - userAmount: 0, - }, - }, - } - : {}), - }; + const user = { + ...req.body, + bio: "", + id: userId, + type: type, + focus: "academic", + status: "active", + desiredLevels: DEFAULT_DESIRED_LEVELS, + profilePicture: "/defaultAvatar.png", + levels: DEFAULT_LEVELS, + isFirstLogin: false, + isVerified: true, + registrationDate: new Date(), + entities: [{ id: entity, role: defaultRole?.id || "" }], + subscriptionExpirationDate: expiryDate || null, + ...((maker.type === "corporate" || maker.type === "mastercorporate") && type === "corporate" + ? { + corporateInformation: {}, + } + : {}), + }; - const uid = new ShortUniqueId(); - const code = uid.randomUUID(6); + const uid = new ShortUniqueId(); + const code = uid.randomUUID(6); - await db.collection("users").insertOne(user); - await db.collection("codes").insertOne({ - code, - creator: maker.id, - expiryDate, - type, - creationDate: new Date(), - userId, - email: email.toLowerCase(), - name: req.body.name, - ...(!!passport_id ? { passport_id } : {}), - }); + await db.collection("users").insertOne(user); + await db.collection("codes").insertOne({ + code, + creator: maker.id, + expiryDate, + type, + creationDate: new Date(), + userId, + email: email.toLowerCase(), + name: req.body.name, + ...(!!passport_id ? { passport_id } : {}), + }); - if (!!groupID) { - const group = await getGroup(groupID); - if (!!group) await db.collection("groups").updateOne({ id: group.id }, { $set: { participants: [...group.participants, userId] } }); - } + if (!!groupID) { + const group = await getGroup(groupID); + if (!!group) await db.collection("groups").updateOne({ id: group.id }, { $set: { participants: [...group.participants, userId] } }); + } - console.log(`Returning - ${email}`); - return res.status(200).json({ ok: true }); - }) - .catch((error) => { - if (error.code.includes("email-already-in-use")) return res.status(403).json({ error, message: "E-mail is already in the platform." }); + console.log(`Returning - ${email}`); + return res.status(200).json({ ok: true }); + }) + .catch((error) => { + if (error.code.includes("email-already-in-use")) return res.status(403).json({ error, message: "E-mail is already in the platform." }); - console.log(`Failing - ${email}`); - console.log(error); - return res.status(401).json({ error }); - }); + console.log(`Failing - ${email}`); + console.log(error); + return res.status(401).json({ error }); + }); } diff --git a/src/pages/api/tickets/index.ts b/src/pages/api/tickets/index.ts index 46bc856f..7a21f801 100644 --- a/src/pages/api/tickets/index.ts +++ b/src/pages/api/tickets/index.ts @@ -58,7 +58,7 @@ async function get(req: NextApiRequest, res: NextApiResponse) { if (admin) { return { ...d, - corporate: admin.corporateInformation?.companyInformation?.name, + corporate: admin.name, }; } diff --git a/src/pages/assignments/index.tsx b/src/pages/assignments/index.tsx index 50f0a14a..ae0c1eca 100644 --- a/src/pages/assignments/index.tsx +++ b/src/pages/assignments/index.tsx @@ -1,7 +1,7 @@ import Layout from "@/components/High/Layout"; import Separator from "@/components/Low/Separator"; -import AssignmentCard from "@/dashboards/AssignmentCard"; -import AssignmentView from "@/dashboards/AssignmentView"; +import AssignmentCard from "@/components/AssignmentCard"; +import AssignmentView from "@/components/AssignmentView"; import { useAllowedEntities } from "@/hooks/useEntityPermissions"; import { useListSearch } from "@/hooks/useListSearch"; import usePagination from "@/hooks/usePagination"; diff --git a/src/pages/dashboard/admin.tsx b/src/pages/dashboard/admin.tsx index d03de7ae..ecfcbfc2 100644 --- a/src/pages/dashboard/admin.tsx +++ b/src/pages/dashboard/admin.tsx @@ -1,7 +1,7 @@ /* eslint-disable @next/next/no-img-element */ import Layout from "@/components/High/Layout"; import UserDisplayList from "@/components/UserDisplayList"; -import IconCard from "@/dashboards/IconCard"; +import IconCard from "@/components/IconCard"; import { Module } from "@/interfaces"; import { EntityWithRoles } from "@/interfaces/entity"; import { Assignment } from "@/interfaces/results"; diff --git a/src/pages/dashboard/corporate.tsx b/src/pages/dashboard/corporate.tsx index b2966c63..1c43c1dc 100644 --- a/src/pages/dashboard/corporate.tsx +++ b/src/pages/dashboard/corporate.tsx @@ -1,7 +1,7 @@ /* eslint-disable @next/next/no-img-element */ import Layout from "@/components/High/Layout"; import UserDisplayList from "@/components/UserDisplayList"; -import IconCard from "@/dashboards/IconCard"; +import IconCard from "@/components/IconCard"; import { Module } from "@/interfaces"; import { EntityWithRoles } from "@/interfaces/entity"; import { Assignment } from "@/interfaces/results"; @@ -25,183 +25,183 @@ import Head from "next/head"; import { useRouter } from "next/router"; import { useMemo } from "react"; import { - BsClipboard2Data, - BsClock, - BsEnvelopePaper, - BsPaperclip, - BsPencilSquare, - BsPeople, - BsPeopleFill, - BsPersonFill, - BsPersonFillGear, + BsClipboard2Data, + BsClock, + BsEnvelopePaper, + BsPaperclip, + BsPencilSquare, + BsPeople, + BsPeopleFill, + BsPersonFill, + BsPersonFillGear, } from "react-icons/bs"; import { ToastContainer } from "react-toastify"; interface Props { - user: User; - users: User[]; - entities: EntityWithRoles[]; - assignments: Assignment[]; - stats: Stat[]; - groups: Group[]; + user: User; + users: User[]; + entities: EntityWithRoles[]; + assignments: Assignment[]; + stats: Stat[]; + groups: Group[]; } export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const user = await requestUser(req, res) if (!user) return redirect("/login") - if (!checkAccess(user, ["admin", "developer", "corporate"])) return redirect("/") + if (!checkAccess(user, ["admin", "developer", "corporate"])) return redirect("/") - const entityIDS = mapBy(user.entities, "id") || []; - const entities = await getEntitiesWithRoles(entityIDS); - const users = await filterAllowedUsers(user, entities) + const entityIDS = mapBy(user.entities, "id") || []; + const entities = await getEntitiesWithRoles(entityIDS); + const users = await filterAllowedUsers(user, entities) - const assignments = await getEntitiesAssignments(entityIDS); - const stats = await getStatsByUsers(users.map((u) => u.id)); - const groups = await getGroupsByEntities(entityIDS); + const assignments = await getEntitiesAssignments(entityIDS); + const stats = await getStatsByUsers(users.map((u) => u.id)); + const groups = await getGroupsByEntities(entityIDS); - return { props: serialize({ user, users, entities, assignments, stats, groups }) }; + return { props: serialize({ user, users, entities, assignments, stats, groups }) }; }, sessionOptions); export default function Dashboard({ user, users, entities, assignments, stats, groups }: Props) { - const students = useMemo(() => users.filter((u) => u.type === "student"), [users]); - const teachers = useMemo(() => users.filter((u) => u.type === "teacher"), [users]); + const students = useMemo(() => users.filter((u) => u.type === "student"), [users]); + const teachers = useMemo(() => users.filter((u) => u.type === "teacher"), [users]); - const router = useRouter(); + const router = useRouter(); - const averageLevelCalculator = (studentStats: Stat[]) => { - const formattedStats = studentStats - .map((s) => ({ - focus: students.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 averageLevelCalculator = (studentStats: Stat[]) => { + const formattedStats = studentStats + .map((s) => ({ + focus: students.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 levels: { [key in Module]: number } = { + reading: 0, + listening: 0, + writing: 0, + speaking: 0, + level: 0, + }; + bandScores.forEach((b) => (levels[b.module] += b.level)); - return calculateAverageLevel(levels); - }; + return calculateAverageLevel(levels); + }; - const UserDisplay = (displayUser: User) => ( -
    - {displayUser.name} -
    - {displayUser.name} - {displayUser.email} -
    -
    - ); + const UserDisplay = (displayUser: User) => ( +
    + {displayUser.name} +
    + {displayUser.name} + {displayUser.email} +
    +
    + ); - return ( - <> - - EnCoach - - - - - - -
    - {entities.length > 0 && ( -
    - {mapBy(entities, "label")?.join(", ")} -
    - )} -
    - router.push("/users?type=student")} - Icon={BsPersonFill} - label="Students" - value={students.length} - color="purple" - /> - router.push("/users?type=teacher")} - Icon={BsPencilSquare} - label="Teachers" - value={teachers.length} - color="purple" - /> - router.push("/classrooms")} - Icon={BsPeople} - label="Classrooms" - value={groups.length} - color="purple" - /> - router.push("/entities")} - label="Entities" - value={entities.length} - color="purple" - /> - - - router.push("/users/performance")} - label="Student Performance" + return ( + <> + + EnCoach + + + + + + +
    + {entities.length > 0 && ( +
    + {mapBy(entities, "label")?.join(", ")} +
    + )} +
    + router.push("/users?type=student")} + Icon={BsPersonFill} + label="Students" value={students.length} color="purple" /> - - router.push("/assignments")} - label="Assignments" - value={assignments.filter((a) => !a.archived).length} - color="purple" - /> -
    -
    + router.push("/users?type=teacher")} + Icon={BsPencilSquare} + label="Teachers" + value={teachers.length} + color="purple" + /> + router.push("/classrooms")} + Icon={BsPeople} + label="Classrooms" + value={groups.length} + color="purple" + /> + router.push("/entities")} + label="Entities" + value={entities.length} + color="purple" + /> + + + router.push("/users/performance")} + label="Student Performance" + value={students.length} + color="purple" + /> + + router.push("/assignments")} + label="Assignments" + value={assignments.filter((a) => !a.archived).length} + color="purple" + /> +
    +
    -
    - dateSorter(a, b, "desc", "registrationDate"))} - title="Latest Students" - /> - dateSorter(a, b, "desc", "registrationDate"))} - title="Latest Teachers" - /> - calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))} - title="Highest level students" - /> - - Object.keys(groupByExam(filterBy(stats, "user", b))).length - - Object.keys(groupByExam(filterBy(stats, "user", a))).length, - ) - } - title="Highest exam count students" - /> -
    -
    - - ); +
    + dateSorter(a, b, "desc", "registrationDate"))} + title="Latest Students" + /> + dateSorter(a, b, "desc", "registrationDate"))} + title="Latest Teachers" + /> + calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))} + title="Highest level students" + /> + + Object.keys(groupByExam(filterBy(stats, "user", b))).length - + Object.keys(groupByExam(filterBy(stats, "user", a))).length, + ) + } + title="Highest exam count students" + /> +
    + + + ); } diff --git a/src/pages/dashboard/developer.tsx b/src/pages/dashboard/developer.tsx index d03de7ae..ecfcbfc2 100644 --- a/src/pages/dashboard/developer.tsx +++ b/src/pages/dashboard/developer.tsx @@ -1,7 +1,7 @@ /* eslint-disable @next/next/no-img-element */ import Layout from "@/components/High/Layout"; import UserDisplayList from "@/components/UserDisplayList"; -import IconCard from "@/dashboards/IconCard"; +import IconCard from "@/components/IconCard"; import { Module } from "@/interfaces"; import { EntityWithRoles } from "@/interfaces/entity"; import { Assignment } from "@/interfaces/results"; diff --git a/src/pages/dashboard/mastercorporate.tsx b/src/pages/dashboard/mastercorporate.tsx index 229c7ce1..1e7eaf5a 100644 --- a/src/pages/dashboard/mastercorporate.tsx +++ b/src/pages/dashboard/mastercorporate.tsx @@ -1,7 +1,7 @@ /* eslint-disable @next/next/no-img-element */ import Layout from "@/components/High/Layout"; import UserDisplayList from "@/components/UserDisplayList"; -import IconCard from "@/dashboards/IconCard"; +import IconCard from "@/components/IconCard"; import { useAllowedEntities } from "@/hooks/useEntityPermissions"; import { Module } from "@/interfaces"; import { EntityWithRoles } from "@/interfaces/entity"; diff --git a/src/pages/dashboard/teacher.tsx b/src/pages/dashboard/teacher.tsx index 36afb919..b86da522 100644 --- a/src/pages/dashboard/teacher.tsx +++ b/src/pages/dashboard/teacher.tsx @@ -1,7 +1,7 @@ /* eslint-disable @next/next/no-img-element */ import Layout from "@/components/High/Layout"; import UserDisplayList from "@/components/UserDisplayList"; -import IconCard from "@/dashboards/IconCard"; +import IconCard from "@/components/IconCard"; import { Module } from "@/interfaces"; import { EntityWithRoles } from "@/interfaces/entity"; import { Assignment } from "@/interfaces/results"; @@ -28,139 +28,139 @@ import { useAllowedEntities } from "@/hooks/useEntityPermissions"; import { filterAllowedUsers } from "@/utils/users.be"; interface Props { - user: User; - users: User[]; - entities: EntityWithRoles[]; - assignments: Assignment[]; - stats: Stat[]; - groups: Group[]; + user: User; + users: User[]; + entities: EntityWithRoles[]; + assignments: Assignment[]; + stats: Stat[]; + groups: Group[]; } export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const user = await requestUser(req, res) if (!user) return redirect("/login") - if (!checkAccess(user, ["admin", "developer", "teacher"])) - return redirect("/") + if (!checkAccess(user, ["admin", "developer", "teacher"])) + return redirect("/") - const entityIDS = mapBy(user.entities, "id") || []; - const entities = await getEntitiesWithRoles(entityIDS); - const users = await filterAllowedUsers(user, entities) + const entityIDS = mapBy(user.entities, "id") || []; + const entities = await getEntitiesWithRoles(entityIDS); + const users = await filterAllowedUsers(user, entities) - const assignments = await getEntitiesAssignments(entityIDS); - const stats = await getStatsByUsers(users.map((u) => u.id)); - const groups = await getGroupsByEntities(entityIDS); + const assignments = await getEntitiesAssignments(entityIDS); + const stats = await getStatsByUsers(users.map((u) => u.id)); + const groups = await getGroupsByEntities(entityIDS); - return { props: serialize({ user, users, entities, assignments, stats, groups }) }; + return { props: serialize({ user, users, entities, assignments, stats, groups }) }; }, sessionOptions); export default function Dashboard({ user, users, entities, assignments, stats, groups }: Props) { - const students = useMemo(() => users.filter((u) => u.type === "student"), [users]); - const router = useRouter(); + const students = useMemo(() => users.filter((u) => u.type === "student"), [users]); + const router = useRouter(); - const averageLevelCalculator = (studentStats: Stat[]) => { - const formattedStats = studentStats - .map((s) => ({ - focus: students.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 averageLevelCalculator = (studentStats: Stat[]) => { + const formattedStats = studentStats + .map((s) => ({ + focus: students.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 levels: { [key in Module]: number } = { + reading: 0, + listening: 0, + writing: 0, + speaking: 0, + level: 0, + }; + bandScores.forEach((b) => (levels[b.module] += b.level)); - return calculateAverageLevel(levels); - }; + return calculateAverageLevel(levels); + }; - const UserDisplay = (displayUser: User) => ( -
    - {displayUser.name} -
    - {displayUser.name} - {displayUser.email} -
    -
    - ); + const UserDisplay = (displayUser: User) => ( +
    + {displayUser.name} +
    + {displayUser.name} + {displayUser.email} +
    +
    + ); - return ( - <> - - EnCoach - - - - - - -
    - {entities.length > 0 && ( -
    - {mapBy(entities, "label")?.join(", ")} -
    - )} -
    - router.push("/users?type=student")} - label="Students" - value={students.length} - color="purple" - /> - router.push("/classrooms")} - Icon={BsPeople} - label="Classrooms" - value={groups.length} - color="purple" - /> - - - router.push("/assignments")} - label="Assignments" - value={assignments.filter((a) => !a.archived).length} - color="purple" - /> -
    -
    + return ( + <> + + EnCoach + + + + + + +
    + {entities.length > 0 && ( +
    + {mapBy(entities, "label")?.join(", ")} +
    + )} +
    + router.push("/users?type=student")} + label="Students" + value={students.length} + color="purple" + /> + router.push("/classrooms")} + Icon={BsPeople} + label="Classrooms" + value={groups.length} + color="purple" + /> + + + router.push("/assignments")} + label="Assignments" + value={assignments.filter((a) => !a.archived).length} + color="purple" + /> +
    +
    -
    - dateSorter(a, b, "desc", "registrationDate"))} - title="Latest Students" - /> - calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))} - title="Highest level students" - /> - - Object.keys(groupByExam(filterBy(stats, "user", b))).length - - Object.keys(groupByExam(filterBy(stats, "user", a))).length, - ) - } - title="Highest exam count students" - /> -
    -
    - - ); +
    + dateSorter(a, b, "desc", "registrationDate"))} + title="Latest Students" + /> + calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels))} + title="Highest level students" + /> + + Object.keys(groupByExam(filterBy(stats, "user", b))).length - + Object.keys(groupByExam(filterBy(stats, "user", a))).length, + ) + } + title="Highest exam count students" + /> +
    +
    + + ); } diff --git a/src/pages/payment-record.tsx b/src/pages/payment-record.tsx index 20b5c081..2be5369a 100644 --- a/src/pages/payment-record.tsx +++ b/src/pages/payment-record.tsx @@ -1,20 +1,20 @@ /* eslint-disable @next/next/no-img-element */ import Head from "next/head"; -import {withIronSessionSsr} from "iron-session/next"; -import {sessionOptions} from "@/lib/session"; +import { withIronSessionSsr } from "iron-session/next"; +import { sessionOptions } from "@/lib/session"; import useUser from "@/hooks/useUser"; -import {toast, ToastContainer} from "react-toastify"; +import { toast, ToastContainer } from "react-toastify"; import Layout from "@/components/High/Layout"; -import {shouldRedirectHome} from "@/utils/navigation.disabled"; +import { shouldRedirectHome } from "@/utils/navigation.disabled"; import usePayments from "@/hooks/usePayments"; import usePaypalPayments from "@/hooks/usePaypalPayments"; -import {Payment, PaypalPayment} from "@/interfaces/paypal"; -import {CellContext, createColumnHelper, flexRender, getCoreRowModel, HeaderGroup, Table, useReactTable} from "@tanstack/react-table"; -import {CURRENCIES} from "@/resources/paypal"; -import {BsTrash} from "react-icons/bs"; +import { Payment, PaypalPayment } from "@/interfaces/paypal"; +import { CellContext, createColumnHelper, flexRender, getCoreRowModel, HeaderGroup, Table, useReactTable } from "@tanstack/react-table"; +import { CURRENCIES } from "@/resources/paypal"; +import { BsTrash } from "react-icons/bs"; import axios from "axios"; -import {useEffect, useState, useMemo} from "react"; -import {AgentUser, CorporateUser, User} from "@/interfaces/user"; +import { useEffect, useState, useMemo } from "react"; +import { AgentUser, CorporateUser, User } from "@/interfaces/user"; import UserCard from "@/components/UserCard"; import Modal from "@/components/Modal"; import clsx from "clsx"; @@ -26,15 +26,15 @@ import Input from "@/components/Low/Input"; import ReactDatePicker from "react-datepicker"; import moment from "moment"; import PaymentAssetManager from "@/components/PaymentAssetManager"; -import {toFixedNumber} from "@/utils/number"; -import {CSVLink} from "react-csv"; -import {Tab} from "@headlessui/react"; -import {useListSearch} from "@/hooks/useListSearch"; -import {checkAccess, getTypesOfUser} from "@/utils/permissions"; +import { toFixedNumber } from "@/utils/number"; +import { CSVLink } from "react-csv"; +import { Tab } from "@headlessui/react"; +import { useListSearch } from "@/hooks/useListSearch"; +import { checkAccess, getTypesOfUser } from "@/utils/permissions"; import { requestUser } from "@/utils/api"; import { redirect } from "@/utils"; -export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { +export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const user = await requestUser(req, res) if (!user) return redirect("/login") @@ -43,18 +43,18 @@ export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { } return { - props: {user}, + props: { user }, }; }, sessionOptions); const columnHelper = createColumnHelper(); const paypalColumnHelper = createColumnHelper(); -const PaymentCreator = ({onClose, reload, showComission = false}: {onClose: () => void; reload: () => void; showComission: boolean}) => { +const PaymentCreator = ({ onClose, reload, showComission = false }: { onClose: () => void; reload: () => void; showComission: boolean }) => { const [corporate, setCorporate] = useState(); const [date, setDate] = useState(new Date()); - const {users} = useUsers(); + const { users } = useUsers(); const price = corporate?.corporateInformation?.payment?.value || 0; const commission = corporate?.corporateInformation?.payment?.commission || 0; @@ -101,13 +101,13 @@ const PaymentCreator = ({onClose, reload, showComission = false}: {onClose: () = options={(users.filter((u) => u.type === "corporate") as CorporateUser[]).map((user) => ({ value: user.id, meta: user, - label: `${user.corporateInformation?.companyInformation?.name || user.name} - ${user.email}`, + label: `${user.name} - ${user.email}`, }))} - defaultValue={{value: "undefined", label: "Select an account"}} + defaultValue={{ value: "undefined", label: "Select an account" }} onChange={(value) => setCorporate((value as any)?.meta ?? undefined)} menuPortalTarget={document?.body} styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), + menuPortal: (base) => ({ ...base, zIndex: 9999 }), control: (styles) => ({ ...styles, paddingLeft: "4px", @@ -129,10 +129,10 @@ const PaymentCreator = ({onClose, reload, showComission = false}: {onClose: () =
    - {}} type="number" value={price} defaultValue={0} className="col-span-3" disabled /> + { }} type="number" value={price} defaultValue={0} className="col-span-3" disabled /> {}} type="number" defaultValue={0} value={commission} disabled /> + { }} type="number" defaultValue={0} value={commission} disabled />
    @@ -277,16 +277,16 @@ export default function PaymentRecord() { const [selectedCorporateUser, setSelectedCorporateUser] = useState(); const [selectedAgentUser, setSelectedAgentUser] = useState(); const [isCreatingPayment, setIsCreatingPayment] = useState(false); - const [filters, setFilters] = useState<{filter: (p: Payment) => boolean; id: string}[]>([]); + const [filters, setFilters] = useState<{ filter: (p: Payment) => boolean; id: string }[]>([]); const [displayPayments, setDisplayPayments] = useState([]); const [corporate, setCorporate] = useState(); const [agent, setAgent] = useState(); - const {user} = useUser({redirectTo: "/login"}); - const {users, reload: reloadUsers} = useUsers(); - const {payments: originalPayments, reload: reloadPayment} = usePayments(); - const {payments: paypalPayments, reload: reloadPaypalPayment} = usePaypalPayments(); + const { user } = useUser({ redirectTo: "/login" }); + const { users, reload: reloadUsers } = useUsers(); + const { payments: originalPayments, reload: reloadPayment } = usePayments(); + const { payments: paypalPayments, reload: reloadPaypalPayment } = usePaypalPayments(); const [startDate, setStartDate] = useState(moment("01/01/2023").toDate()); const [endDate, setEndDate] = useState(moment().endOf("day").toDate()); @@ -331,11 +331,11 @@ export default function PaymentRecord() { ...(!agent ? [] : [ - { - id: "agent-filter", - filter: (p: Payment) => p.agent === agent.id, - }, - ]), + { + id: "agent-filter", + filter: (p: Payment) => p.agent === agent.id, + }, + ]), ]); }, [agent]); @@ -345,18 +345,18 @@ export default function PaymentRecord() { ...(!corporate ? [] : [ - { - id: "corporate-filter", - filter: (p: Payment) => p.corporate === corporate.id, - }, - ]), + { + id: "corporate-filter", + filter: (p: Payment) => p.corporate === corporate.id, + }, + ]), ]); }, [corporate]); useEffect(() => { setFilters((prev) => [ ...prev.filter((x) => x.id !== "paid"), - ...(typeof paid !== "boolean" ? [] : [{id: "paid", filter: (p: Payment) => p.isPaid === paid}]), + ...(typeof paid !== "boolean" ? [] : [{ id: "paid", filter: (p: Payment) => p.isPaid === paid }]), ]); }, [paid]); @@ -366,11 +366,11 @@ export default function PaymentRecord() { ...(typeof commissionTransfer !== "boolean" ? [] : [ - { - id: "commissionTransfer", - filter: (p: Payment) => !p.commissionTransfer === commissionTransfer, - }, - ]), + { + id: "commissionTransfer", + filter: (p: Payment) => !p.commissionTransfer === commissionTransfer, + }, + ]), ]); }, [commissionTransfer]); @@ -380,11 +380,11 @@ export default function PaymentRecord() { ...(typeof corporateTransfer !== "boolean" ? [] : [ - { - id: "corporateTransfer", - filter: (p: Payment) => !p.corporateTransfer === corporateTransfer, - }, - ]), + { + id: "corporateTransfer", + filter: (p: Payment) => !p.corporateTransfer === corporateTransfer, + }, + ]), ]); }, [corporateTransfer]); @@ -395,7 +395,7 @@ export default function PaymentRecord() { const updatePayment = (payment: Payment, key: string, value: any) => { axios - .patch(`api/payments/${payment.id}`, {...payment, [key]: value}) + .patch(`api/payments/${payment.id}`, { ...payment, [key]: value }) .then(() => toast.success("Updated the payment")) .finally(reload); }; @@ -540,7 +540,7 @@ export default function PaymentRecord() { switch (key) { case "agentCommission": { const value = info.getValue(); - return {value: `${value}%`}; + return { value: `${value}%` }; } case "agent": { const user = users.find((x) => x.id === info.row.original.agent) as AgentUser; @@ -553,18 +553,18 @@ export default function PaymentRecord() { case "amount": { const value = info.getValue(); const numberValue = toFixedNumber(value, 2); - return {value: numberValue}; + return { value: numberValue }; } case "date": { const value = info.getValue(); - return {value: moment(value).format("DD/MM/YYYY")}; + return { value: moment(value).format("DD/MM/YYYY") }; } case "corporate": { const specificValue = info.row.original.corporate; const user = users.find((x) => x.id === specificValue) as CorporateUser; return { user, - value: user?.corporateInformation.companyInformation.name || user?.name, + value: user?.name, }; } case "currency": { @@ -576,7 +576,7 @@ export default function PaymentRecord() { case "corporateId": default: { const value = info.getValue(); - return {value}; + return { value }; } } }; @@ -588,7 +588,7 @@ export default function PaymentRecord() { header: "Country Manager", id: "agent", cell: (info) => { - const {user, value} = columHelperValue(info.column.id, info); + const { user, value } = columHelperValue(info.column.id, info); return (
    { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); return <>{value}; }, }), @@ -612,7 +612,7 @@ export default function PaymentRecord() { header: "Commission Value", id: "agentValue", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); const finalValue = `${value} ${info.row.original.currency}`; return {finalValue}; }, @@ -626,7 +626,7 @@ export default function PaymentRecord() { header: "Corporate ID", id: "corporateId", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); return value; }, }), @@ -634,7 +634,7 @@ export default function PaymentRecord() { header: "Corporate", id: "corporate", cell: (info) => { - const {user, value} = columHelperValue(info.column.id, info); + const { user, value } = columHelperValue(info.column.id, info); return (
    { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); return {value}; }, }), @@ -658,7 +658,7 @@ export default function PaymentRecord() { header: "Amount", id: "amount", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); const currency = CURRENCIES.find((x) => x.currency === info.row.original.currency)?.label; const finalValue = `${value} ${currency}`; return {finalValue}; @@ -669,7 +669,7 @@ export default function PaymentRecord() { header: "Paid", id: "isPaid", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); return ( { + cell: ({ row }: { row: { original: Payment } }) => { return (
    {user?.type !== "agent" && ( @@ -720,7 +720,7 @@ export default function PaymentRecord() { }) .map((p) => { const user = users.find((x) => x.id === p.userId) as User; - return {...p, name: user?.name, email: user?.email}; + return { ...p, name: user?.name, email: user?.email }; }), [paypalPayments, users, startDatePaymob, endDatePaymob], ); @@ -730,7 +730,7 @@ export default function PaymentRecord() { header: "Order ID", id: "orderId", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); return {value}; }, }), @@ -738,7 +738,7 @@ export default function PaymentRecord() { header: "Status", id: "status", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); return {value}; }, }), @@ -746,7 +746,7 @@ export default function PaymentRecord() { header: "User Name", id: "name", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); return {value}; }, }), @@ -754,7 +754,7 @@ export default function PaymentRecord() { header: "Email", id: "email", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); return {value}; }, }), @@ -762,7 +762,7 @@ export default function PaymentRecord() { header: "Amount", id: "value", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); const finalValue = `${value} ${info.row.original.currency}`; return {finalValue}; }, @@ -771,7 +771,7 @@ export default function PaymentRecord() { header: "Date", id: "createdAt", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); return {moment(value).format("DD/MM/YYYY")}; }, }), @@ -779,13 +779,13 @@ export default function PaymentRecord() { header: "Expiration Date", id: "subscriptionExpirationDate", cell: (info) => { - const {value} = columHelperValue(info.column.id, info); + const { value } = columHelperValue(info.column.id, info); return {moment(value).format("DD/MM/YYYY")}; }, }), ]; - const {rows: filteredRows, renderSearch} = useListSearch(paypalFilterRows, updatedPaypalPayments); + const { rows: filteredRows, renderSearch } = useListSearch(paypalFilterRows, updatedPaypalPayments); const paypalTable = useReactTable({ data: filteredRows.sort((a, b) => moment(b.createdAt).diff(moment(a.createdAt), "second")), @@ -809,7 +809,7 @@ export default function PaymentRecord() { }} user={selectedCorporateUser} disabled - disabledFields={{countryManager: true}} + disabledFields={{ countryManager: true }} />
    )} @@ -859,7 +859,7 @@ export default function PaymentRecord() { return [...accm, ...data]; }, []); - const {rows} = currentTable.getRowModel(); + const { rows } = currentTable.getRowModel(); const finalColumns = [ ...columns, @@ -872,8 +872,8 @@ export default function PaymentRecord() { return { columns: finalColumns, rows: rows.map((row) => { - return finalColumns.reduce((accm, {key}) => { - const {value} = columHelperValue(key, { + return finalColumns.reduce((accm, { key }) => { + const { value } = columHelperValue(key, { row, getValue: () => row.getValue(key), }); @@ -886,7 +886,7 @@ export default function PaymentRecord() { }; }; - const {rows: csvRows, columns: csvColumns} = getCSVData(); + const { rows: csvRows, columns: csvColumns } = getCSVData(); const renderTable = (table: Table) => ( @@ -958,7 +958,7 @@ export default function PaymentRecord() { + className={({ selected }) => clsx( "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-mti-purple-light", "ring-white ring-opacity-60 ring-offset-2 ring-offset-mti-purple-light focus:outline-none focus:ring-2", @@ -970,7 +970,7 @@ export default function PaymentRecord() { {checkAccess(user, ["developer", "admin"]) && ( + className={({ selected }) => clsx( "w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-mti-purple-light", "ring-white ring-opacity-60 ring-offset-2 ring-offset-mti-purple-light focus:outline-none focus:ring-2", @@ -996,24 +996,22 @@ export default function PaymentRecord() { options={(users.filter((u) => u.type === "corporate") as CorporateUser[]).map((user) => ({ value: user.id, meta: user, - label: `${user.corporateInformation?.companyInformation?.name || user.name} - ${user.email}`, + label: `${user.name} - ${user.email}`, }))} defaultValue={ user.type === "corporate" ? { - value: user.id, - meta: user, - label: `${user.corporateInformation?.companyInformation?.name || user.name} - ${ - user.email - }`, - } + value: user.id, + meta: user, + label: `${user.name} - ${user.email}`, + } : undefined } isDisabled={user.type === "corporate"} onChange={(value) => setCorporate((value as any)?.meta ?? undefined)} menuPortalTarget={document?.body} styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), + menuPortal: (base) => ({ ...base, zIndex: 9999 }), control: (styles) => ({ ...styles, paddingLeft: "4px", @@ -1049,15 +1047,15 @@ export default function PaymentRecord() { value={ agent ? { - value: agent?.id, - label: `${agent.name} - ${agent.email}`, - } + value: agent?.id, + label: `${agent.name} - ${agent.email}`, + } : undefined } onChange={(value) => setAgent(value !== null ? (value as any).meta : undefined)} menuPortalTarget={document?.body} styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), + menuPortal: (base) => ({ ...base, zIndex: 9999 }), control: (styles) => ({ ...styles, paddingLeft: "4px", @@ -1092,7 +1090,7 @@ export default function PaymentRecord() { }} menuPortalTarget={document?.body} styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), + menuPortal: (base) => ({ ...base, zIndex: 9999 }), control: (styles) => ({ ...styles, paddingLeft: "4px", @@ -1149,7 +1147,7 @@ export default function PaymentRecord() { }} menuPortalTarget={document?.body} styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), + menuPortal: (base) => ({ ...base, zIndex: 9999 }), control: (styles) => ({ ...styles, paddingLeft: "4px", @@ -1183,7 +1181,7 @@ export default function PaymentRecord() { }} menuPortalTarget={document?.body} styles={{ - menuPortal: (base) => ({...base, zIndex: 9999}), + menuPortal: (base) => ({ ...base, zIndex: 9999 }), control: (styles) => ({ ...styles, paddingLeft: "4px", diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index 40e98fe9..9bb4c9f9 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -1,16 +1,16 @@ /* eslint-disable @next/next/no-img-element */ import Head from "next/head"; -import {withIronSessionSsr} from "iron-session/next"; -import {sessionOptions} from "@/lib/session"; -import {ChangeEvent, Dispatch, ReactNode, SetStateAction, useEffect, useRef, useState} from "react"; +import { withIronSessionSsr } from "iron-session/next"; +import { sessionOptions } from "@/lib/session"; +import { ChangeEvent, Dispatch, ReactNode, SetStateAction, useEffect, useRef, useState } from "react"; import useUser from "@/hooks/useUser"; -import {toast, ToastContainer} from "react-toastify"; +import { toast, ToastContainer } from "react-toastify"; import Layout from "@/components/High/Layout"; import Input from "@/components/Low/Input"; import Button from "@/components/Low/Button"; import Link from "next/link"; import axios from "axios"; -import {ErrorMessage} from "@/constants/errors"; +import { ErrorMessage } from "@/constants/errors"; import clsx from "clsx"; import { CorporateUser, @@ -23,32 +23,32 @@ import { Group, } from "@/interfaces/user"; import CountrySelect from "@/components/Low/CountrySelect"; -import {shouldRedirectHome} from "@/utils/navigation.disabled"; +import { shouldRedirectHome } from "@/utils/navigation.disabled"; import moment from "moment"; -import {BsCamera, BsQuestionCircleFill} from "react-icons/bs"; -import {USER_TYPE_LABELS} from "@/resources/user"; +import { BsCamera, BsQuestionCircleFill } from "react-icons/bs"; +import { USER_TYPE_LABELS } from "@/resources/user"; import useGroups from "@/hooks/useGroups"; import useUsers from "@/hooks/useUsers"; -import {convertBase64, redirect} from "@/utils"; -import {Divider} from "primereact/divider"; +import { convertBase64, redirect } from "@/utils"; +import { Divider } from "primereact/divider"; import GenderInput from "@/components/High/GenderInput"; import EmploymentStatusInput from "@/components/High/EmploymentStatusInput"; import TimezoneSelect from "@/components/Low/TImezoneSelect"; import Modal from "@/components/Modal"; -import {Module} from "@/interfaces"; +import { Module } from "@/interfaces"; import ModuleLevelSelector from "@/components/Medium/ModuleLevelSelector"; import Select from "@/components/Low/Select"; -import {InstructorGender} from "@/interfaces/exam"; -import {capitalize} from "lodash"; +import { InstructorGender } from "@/interfaces/exam"; +import { capitalize } from "lodash"; import TopicModal from "@/components/Medium/TopicModal"; -import {v4} from "uuid"; -import {checkAccess, getTypesOfUser} from "@/utils/permissions"; -import {getParticipantGroups, getUserCorporate} from "@/utils/groups.be"; -import {InferGetServerSidePropsType} from "next"; -import {getUsers} from "@/utils/users.be"; +import { v4 } from "uuid"; +import { checkAccess, getTypesOfUser } from "@/utils/permissions"; +import { getParticipantGroups, getUserCorporate } from "@/utils/groups.be"; +import { InferGetServerSidePropsType } from "next"; +import { getUsers } from "@/utils/users.be"; import { requestUser } from "@/utils/api"; -export const getServerSideProps = withIronSessionSsr(async ({req, res}) => { +export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { const user = await requestUser(req, res) if (!user) return redirect("/login") @@ -72,9 +72,9 @@ interface Props { linkedCorporate?: CorporateUser | MasterCorporateUser; } -const DoubleColumnRow = ({children}: {children: ReactNode}) =>
    {children}
    ; +const DoubleColumnRow = ({ children }: { children: ReactNode }) =>
    {children}
    ; -function UserProfile({user, mutateUser, linkedCorporate, groups, users}: Props) { +function UserProfile({ user, mutateUser, linkedCorporate, groups, users }: Props) { const [bio, setBio] = useState(user.bio || ""); const [name, setName] = useState(user.name || ""); const [email, setEmail] = useState(user.email || ""); @@ -182,21 +182,21 @@ function UserProfile({user, mutateUser, linkedCorporate, groups, users}: Props) passport_id, timezone, }, - ...(user.type === "corporate" ? {corporateInformation} : {}), + ...(user.type === "corporate" ? { corporateInformation } : {}), ...(user.type === "agent" ? { - agentInformation: { - companyName, - commercialRegistration, - arabName, - }, - } + agentInformation: { + companyName, + commercialRegistration, + arabName, + }, + } : {}), }) .then((response) => { if (response.status === 200) { toast.success("Your profile has been updated!"); - mutateUser((response.data as {user: User}).user); + mutateUser((response.data as { user: User }).user); setIsLoading(false); return; } @@ -247,7 +247,7 @@ function UserProfile({user, mutateUser, linkedCorporate, groups, users}: Props)

    Edit Profile

    e.preventDefault()}> - {user.type !== "corporate" && user.type !== "mastercorporate" ? ( + {user.type !== "corporate" && user.type !== "mastercorporate" && ( - ) : ( - - setCorporateInformation((prev) => ({ - ...prev!, - companyInformation: { - ...prev!.companyInformation, - name: e, - }, - })) - } - placeholder="Enter your company's name" - defaultValue={corporateInformation?.companyInformation?.name} - required - /> )} {user.type === "agent" && ( @@ -381,7 +362,7 @@ function UserProfile({user, mutateUser, linkedCorporate, groups, users}: Props) >} + setLevels={setDesiredLevels as Dispatch>} />
    @@ -425,9 +406,9 @@ function UserProfile({user, mutateUser, linkedCorporate, groups, users}: Props) }} onChange={(value) => (value ? setPreferredGender(value.value as InstructorGender) : null)} options={[ - {value: "male", label: "Male"}, - {value: "female", label: "Female"}, - {value: "varied", label: "Varied"}, + { value: "male", label: "Male" }, + { value: "female", label: "Female" }, + { value: "varied", label: "Varied" }, ]} />
    @@ -461,15 +442,6 @@ function UserProfile({user, mutateUser, linkedCorporate, groups, users}: Props) {user.type === "corporate" && ( <> - null} - label="Number of users" - defaultValue={user.corporateInformation?.companyInformation.userAmount} - disabled - required - /> diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index d36143f9..df2b50ec 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -16,7 +16,7 @@ import { checkAccess, getTypesOfUser } from "@/utils/permissions"; import usePermissions from "@/hooks/usePermissions"; import { useState } from "react"; import Modal from "@/components/Modal"; -import IconCard from "@/dashboards/IconCard"; +import IconCard from "@/components/IconCard"; import { BsCode, BsCodeSquare, BsGearFill, BsPeopleFill, BsPersonFill } from "react-icons/bs"; import UserCreator from "./(admin)/UserCreator"; import CorporateGradingSystem from "./(admin)/CorporateGradingSystem"; diff --git a/src/pages/v1/index.tsx b/src/pages/v1/index.tsx deleted file mode 100644 index 514b4091..00000000 --- a/src/pages/v1/index.tsx +++ /dev/null @@ -1,199 +0,0 @@ -/* eslint-disable @next/next/no-img-element */ -import Head from "next/head"; -import Navbar from "@/components/Navbar"; -import { BsFileEarmarkText, BsPencil, BsStar, BsBook, BsHeadphones, BsPen, BsMegaphone } from "react-icons/bs"; -import { withIronSessionSsr } from "iron-session/next"; -import { sessionOptions } from "@/lib/session"; -import { useEffect, useState } from "react"; -import { averageScore, groupBySession, totalExams } from "@/utils/stats"; -import useUser from "@/hooks/useUser"; -import Diagnostic from "@/components/Diagnostic"; -import { ToastContainer } from "react-toastify"; -import { capitalize } from "lodash"; -import { Module } from "@/interfaces"; -import ProgressBar from "@/components/Low/ProgressBar"; -import Layout from "@/components/High/Layout"; -import { calculateAverageLevel } from "@/utils/score"; -import axios from "axios"; -import DemographicInformationInput from "@/components/DemographicInformationInput"; -import moment from "moment"; -import Link from "next/link"; -import { MODULE_ARRAY } from "@/utils/moduleUtils"; -import ProfileSummary from "@/components/ProfileSummary"; -import StudentDashboard from "@/dashboards/Student"; -import AdminDashboard from "@/dashboards/Admin"; -import CorporateDashboard from "@/dashboards/Corporate"; -import TeacherDashboard from "@/dashboards/Teacher"; -import AgentDashboard from "@/dashboards/Agent"; -import MasterCorporateDashboard from "@/dashboards/MasterCorporate"; -import PaymentDue from "../(status)/PaymentDue"; -import { useRouter } from "next/router"; -import { PayPalScriptProvider } from "@paypal/react-paypal-js"; -import { CorporateUser, MasterCorporateUser, Type, User, userTypes } from "@/interfaces/user"; -import Select from "react-select"; -import { USER_TYPE_LABELS } from "@/resources/user"; -import { checkAccess, getTypesOfUser } from "@/utils/permissions"; -import { getUserCorporate } from "@/utils/groups.be"; -import { getUsers } from "@/utils/users.be"; -import { requestUser } from "@/utils/api"; -import { redirect, serialize } from "@/utils"; - -export const getServerSideProps = withIronSessionSsr(async ({ req, res }) => { - const user = await requestUser(req, res) - if (!user) return redirect("/login") - - const linkedCorporate = (await getUserCorporate(user.id)) || null; - - return { - props: serialize({ user, linkedCorporate }), - }; -}, sessionOptions); - -interface Props { - user: User; - linkedCorporate?: CorporateUser | MasterCorporateUser; -} - -export default function Home({ user: propsUser, linkedCorporate }: Props) { - const [user, setUser] = useState(propsUser); - const [showDiagnostics, setShowDiagnostics] = useState(false); - const [showDemographicInput, setShowDemographicInput] = useState(false); - const [selectedScreen, setSelectedScreen] = useState("admin"); - - const { mutateUser } = useUser({ redirectTo: "/login" }); - const router = useRouter(); - - useEffect(() => { - if (user) { - // setShowDemographicInput(!user.demographicInformation || !user.demographicInformation.country || !user.demographicInformation.phone); - setShowDiagnostics(user.isFirstLogin && user.type === "student"); - } - }, [user]); - - const checkIfUserExpired = () => { - const expirationDate = user!.subscriptionExpirationDate; - - if (expirationDate === null || expirationDate === undefined) return false; - if (moment(expirationDate).isAfter(moment(new Date()))) return false; - - return true; - }; - - if (user && (user.status === "paymentDue" || user.status === "disabled" || checkIfUserExpired())) { - return ( - <> - - EnCoach - - - - - {user.status === "disabled" && ( - -
    - Your account has been disabled! - Please contact an administrator if you believe this to be a mistake. -
    -
    - )} - {(user.status === "paymentDue" || checkIfUserExpired()) && } - - ); - } - - if (user && showDemographicInput) { - return ( - <> - - EnCoach - - - - - - { - setUser(user); - mutateUser(user); - }} - user={user} - /> - - - ); - } - - if (user && showDiagnostics) { - return ( - <> - - EnCoach - - - - - - setShowDiagnostics(false)} /> - - - ); - } - - return ( - <> - - EnCoach - - - - - - {user && ( - - {checkAccess(user, ["student"]) && } - {checkAccess(user, ["teacher"]) && } - {checkAccess(user, ["corporate"]) && } - {checkAccess(user, ["mastercorporate"]) && } - {checkAccess(user, ["agent"]) && } - {checkAccess(user, ["admin"]) && } - {checkAccess(user, ["developer"]) && ( - <> - +
    +
    + Entity Label: + +
    + +
    + Licenses: + setLicenses(parseInt(v))} type="number" placeholder="12" /> +
    diff --git a/src/pages/entities/index.tsx b/src/pages/entities/index.tsx index 4674ada8..8f588ced 100644 --- a/src/pages/entities/index.tsx +++ b/src/pages/entities/index.tsx @@ -6,7 +6,7 @@ import { ToastContainer } from "react-toastify"; import Layout from "@/components/High/Layout"; import { GroupWithUsers, User } from "@/interfaces/user"; import { shouldRedirectHome } from "@/utils/navigation.disabled"; -import { getUserName } from "@/utils/users"; +import { getUserName, isAdmin } from "@/utils/users"; import { convertToUsers, getGroupsForUser } from "@/utils/groups.be"; import { countEntityUsers, getEntityUsers, getSpecificUsers, getUsers } from "@/utils/users.be"; import { checkAccess, findAllowedEntities, getTypesOfUser } from "@/utils/permissions"; @@ -64,7 +64,7 @@ export default function Home({ user, entities }: Props) { Members - {count} + {count}{isAdmin(user) && ` / ${entity.licenses || 0}`} {users.map(getUserName).join(", ")}{' '}