From 6e420a8a8210678d6124f2b6e369eba1fa2411c0 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Tue, 21 Nov 2023 18:01:45 +0000 Subject: [PATCH] Created a dashboard for the Agent --- package.json | 1 + src/components/UserCard.tsx | 107 +++++++++++----- src/dashboards/Agent.tsx | 141 ++++----------------- src/interfaces/user.ts | 2 - src/pages/(register)/RegisterCorporate.tsx | 2 - yarn.lock | 5 + 6 files changed, 101 insertions(+), 157 deletions(-) diff --git a/package.json b/package.json index 5af650df..3a5f5ef8 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "random-words": "^2.0.0", "react": "18.2.0", "react-chartjs-2": "^5.2.0", + "react-currency-input-field": "^3.6.12", "react-datepicker": "^4.18.0", "react-dom": "18.2.0", "react-firebase-hooks": "^5.1.1", diff --git a/src/components/UserCard.tsx b/src/components/UserCard.tsx index 241e09b3..b45c7d4b 100644 --- a/src/components/UserCard.tsx +++ b/src/components/UserCard.tsx @@ -6,7 +6,7 @@ import axios from "axios"; import clsx from "clsx"; import moment from "moment"; import {Divider} from "primereact/divider"; -import {useState} from "react"; +import {useEffect, useState} from "react"; import ReactDatePicker from "react-datepicker"; import {BsFileEarmarkText, BsPencil, BsStar} from "react-icons/bs"; import {toast} from "react-toastify"; @@ -41,15 +41,46 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers}: const [referralAgent, setReferralAgent] = useState(user.corporateInformation?.referralAgent); const [type, setType] = useState(user.type); const [status, setStatus] = useState(user.status); + const [companyName, setCompanyName] = useState(user.corporateInformation?.companyInformation.name); + const [userAmount, setUserAmount] = useState(user.corporateInformation?.companyInformation.userAmount); + const [referralAgentLabel, setReferralAgentLabel] = useState(); const {stats} = useStats(user.id); const {users} = useUsers(); + useEffect(() => { + if (users && users.length > 0) { + if (!referralAgent) { + setReferralAgentLabel("No manager"); + return; + } + + const agent = users.find((x) => x.id === referralAgent); + setReferralAgentLabel(`${agent?.name} - ${agent?.email}`); + } + }, [users, referralAgent]); + const updateUser = () => { if (!confirm(`Are you sure you want to update ${user.name}'s account?`)) return; + // TODO: Add the corporate information when it is changed as well axios - .post<{user?: User; ok?: boolean}>(`/api/users/update?id=${user.id}`, {...user, subscriptionExpirationDate: expiryDate, type, status}) + .post<{user?: User; ok?: boolean}>(`/api/users/update?id=${user.id}`, { + ...user, + subscriptionExpirationDate: expiryDate, + type, + status, + corporateInformation: + type === "corporate" + ? { + referralAgent, + companyInformation: { + companyName, + userAmount, + }, + } + : undefined, + }) .then(() => { toast.success("User updated successfully!"); onClose(true); @@ -269,54 +300,60 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers}: {user.type === "corporate" && ( <> -
+
null} + onChange={setCompanyName} placeholder="Enter company name" - defaultValue={user.corporateInformation?.companyInformation.name} + defaultValue={companyName} /> null} + onChange={(e) => setUserAmount(e ? parseInt(e) : undefined)} placeholder="Enter amount of users" - defaultValue={user.corporateInformation?.companyInformation.userAmount} + defaultValue={userAmount} />
- - u.type === "agent").map((x) => ({value: x.id, label: `${x.name} - ${x.email}`})), + ]} + defaultValue={{ + value: referralAgent, + label: referralAgentLabel, + }} + onChange={(value) => setReferralAgent(value?.value)} + styles={{ + control: (styles) => ({ + ...styles, + paddingLeft: "4px", + border: "none", outline: "none", - }, - }), - option: (styles, state) => ({ - ...styles, - backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white", - color: state.isFocused ? "black" : styles.color, - }), - }} - /> + ":focus": { + outline: "none", + }, + }), + option: (styles, state) => ({ + ...styles, + backgroundColor: state.isFocused ? "#D5D9F0" : state.isSelected ? "#7872BF" : "white", + color: state.isFocused ? "black" : styles.color, + }), + }} + /> + )} +
+ +
+
diff --git a/src/dashboards/Agent.tsx b/src/dashboards/Agent.tsx index a08ba4a6..b6dd37a5 100644 --- a/src/dashboards/Agent.tsx +++ b/src/dashboards/Agent.tsx @@ -47,10 +47,9 @@ export default function AgentDashboard({user}: Props) { setShowModal(!!selectedUser && page === ""); }, [selectedUser, page]); - const studentFilter = (user: User) => user.type === "student" && groups.flatMap((g) => g.participants).includes(user.id); - const teacherFilter = (user: User) => user.type === "teacher" && groups.flatMap((g) => g.participants).includes(user.id); - - const getStatsByStudent = (user: User) => stats.filter((s) => s.user === user.id); + const corporateFilter = (user: User) => user.type === "corporate"; + const referredCorporateFilter = (x: User) => + x.type === "corporate" && !!x.corporateInformation && x.corporateInformation.referralAgent === user.id; const UserDisplay = (displayUser: User) => (
); - const StudentsList = () => { - const filter = (x: User) => - x.type === "student" && - (!!selectedUser - ? groups - .filter((g) => g.admin === selectedUser.id) - .flatMap((g) => g.participants) - .includes(x.id) || false - : groups.flatMap((g) => g.participants).includes(x.id)); + const ReferredCorporateList = () => { + const filter = (x: User) => x.type === "corporate" && !!x.corporateInformation && x.corporateInformation.referralAgent === user.id; return ( <> @@ -83,7 +75,7 @@ export default function AgentDashboard({user}: Props) { Back
-

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

+

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

@@ -91,15 +83,8 @@ export default function AgentDashboard({user}: Props) { ); }; - const TeachersList = () => { - const filter = (x: User) => - x.type === "teacher" && - (!!selectedUser - ? groups - .filter((g) => g.admin === selectedUser.id) - .flatMap((g) => g.participants) - .includes(x.id) || false - : groups.flatMap((g) => g.participants).includes(x.id)); + const CorporateList = () => { + const filter = (x: User) => x.type === "corporate"; return ( <> @@ -110,7 +95,7 @@ export default function AgentDashboard({user}: Props) { Back -

Teachers ({users.filter(filter).length})

+

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

@@ -118,85 +103,31 @@ export default function AgentDashboard({user}: Props) { ); }; - const GroupsList = () => { - const filter = (x: Group) => x.admin === user.id || x.participants.includes(user.id); - - return ( - <> -
-
setPage("")} - className="flex gap-2 items-center text-mti-purple-light cursor-pointer hover:text-mti-purple-dark transition ease-in-out duration-300"> - - Back -
-

Groups ({groups.filter(filter).length})

-
- - - - ); - }; - - const averageLevelCalculator = (studentStats: Stat[]) => { - const formattedStats = studentStats - .map((s) => ({focus: users.find((u) => u.id === s.user)?.focus, score: s.score, module: s.module})) - .filter((f) => !!f.focus); - const bandScores = formattedStats.map((s) => ({ - module: s.module, - level: calculateBandScore(s.score.correct, s.score.total, s.module, s.focus!), - })); - - const levels: {[key in Module]: number} = {reading: 0, listening: 0, writing: 0, speaking: 0, level: 0}; - bandScores.forEach((b) => (levels[b.module] += b.level)); - - return calculateAverageLevel(levels); - }; - const DefaultDashboard = () => ( <> -
+
setPage("students")} + onClick={() => setPage("referredCorporate")} Icon={BsPersonFill} - label="Students" - value={users.filter(studentFilter).length} + label="Referred Corporate" + value={users.filter(referredCorporateFilter).length} color="purple" /> setPage("teachers")} - Icon={BsPersonLinesFill} - label="Teachers" - value={users.filter(teacherFilter).length} + onClick={() => setPage("corporate")} + Icon={BsPersonFill} + label="Corporate" + value={users.filter(corporateFilter).length} color="purple" /> - groups.flatMap((g) => g.participants).includes(s.user)).length} - color="purple" - /> - groups.flatMap((g) => g.participants).includes(s.user))).toFixed(1)} - color="purple" - /> - setPage("groups")} Icon={BsPersonAdd} label="Groups" value={groups.length} color="purple" /> -
- Latest students + Latest Referred Corporate
{users - .filter(studentFilter) + .filter(referredCorporateFilter) .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) .map((x) => ( @@ -204,41 +135,16 @@ export default function AgentDashboard({user}: Props) {
- Latest teachers + Latest corporate
{users - .filter(teacherFilter) + .filter(corporateFilter) .sort((a, b) => dateSorter(a, b, "desc", "registrationDate")) .map((x) => ( ))}
-
- Highest level students -
- {users - .filter(studentFilter) - .sort((a, b) => calculateAverageLevel(b.levels) - calculateAverageLevel(a.levels)) - .map((x) => ( - - ))} -
-
-
- Highest exam count students -
- {users - .filter(studentFilter) - .sort( - (a, b) => - Object.keys(groupByExam(getStatsByStudent(b))).length - Object.keys(groupByExam(getStatsByStudent(a))).length, - ) - .map((x) => ( - - ))} -
-
); @@ -265,9 +171,8 @@ export default function AgentDashboard({user}: Props) { )} - {page === "students" && } - {page === "teachers" && } - {page === "groups" && } + {page === "referredCorporate" && } + {page === "corporate" && } {page === "" && } ); diff --git a/src/interfaces/user.ts b/src/interfaces/user.ts index ab30c5bc..20852817 100644 --- a/src/interfaces/user.ts +++ b/src/interfaces/user.ts @@ -26,9 +26,7 @@ export interface CorporateInformation { value: number; currency: string; }; - monthlyDuration: number; referralAgent?: string; - allowedUserAmount?: number; } export interface CompanyInformation { diff --git a/src/pages/(register)/RegisterCorporate.tsx b/src/pages/(register)/RegisterCorporate.tsx index 89d1af81..c9ff52f7 100644 --- a/src/pages/(register)/RegisterCorporate.tsx +++ b/src/pages/(register)/RegisterCorporate.tsx @@ -68,8 +68,6 @@ export default function RegisterCorporate({isLoading, setIsLoading, mutateUser, userAmount: companyUsers, }, referralAgent, - allowedUserAmount: companyUsers, - monthlyDuration: subscriptionDuration, }, }) .then((response) => { diff --git a/yarn.lock b/yarn.lock index 80f7a18e..d9528ad1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4431,6 +4431,11 @@ react-chartjs-2@^5.2.0: resolved "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz" integrity sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA== +react-currency-input-field@^3.6.12: + version "3.6.12" + resolved "https://registry.yarnpkg.com/react-currency-input-field/-/react-currency-input-field-3.6.12.tgz#6c59bec50b9a769459c971f94f9a67b7bf9046f7" + integrity sha512-92mVEo1u7tF8Lz5JeaEHpQY/p6ulmnfSk9r3dVMyykQNLoScvgQ7GczvV3uGDr81xkTF3czj7CTJ9Ekqq4+pIA== + react-datepicker@^4.18.0: version "4.18.0" resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.18.0.tgz#d66301acc47833d31fa6f46f98781b084106da0e"