From d94a9bb88a9e092ffb2c737d7e598162243a6ddf Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Thu, 15 Aug 2024 18:39:17 +0100 Subject: [PATCH 01/16] Quick little fix --- src/dashboards/AssignmentCreator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dashboards/AssignmentCreator.tsx b/src/dashboards/AssignmentCreator.tsx index b62bd856..aa4cc716 100644 --- a/src/dashboards/AssignmentCreator.tsx +++ b/src/dashboards/AssignmentCreator.tsx @@ -371,7 +371,7 @@ export default function AssignmentCreator({isCreating, assignment, assigner, gro !startDate || !endDate || assignees.length === 0 || - (!!examIDs && examIDs.length < selectedModules.length) + (!useRandomExams && examIDs.length < selectedModules.length) } className="w-full max-w-[200px]" onClick={createAssignment} From 85f684dff547217a02630c692819c1a19f5ac199 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Thu, 15 Aug 2024 19:24:08 +0100 Subject: [PATCH 02/16] Updated the user balance to be based not only on the amount of codes --- src/dashboards/Corporate.tsx | 11 +- src/pages/api/code/index.ts | 262 +++++++++++++++++------------------ 2 files changed, 134 insertions(+), 139 deletions(-) diff --git a/src/dashboards/Corporate.tsx b/src/dashboards/Corporate.tsx index be87e851..5c8daa26 100644 --- a/src/dashboards/Corporate.tsx +++ b/src/dashboards/Corporate.tsx @@ -57,6 +57,7 @@ export default function CorporateDashboard({user}: Props) { const [corporateUserToShow, setCorporateUserToShow] = useState(); const [selectedAssignment, setSelectedAssignment] = useState(); const [isCreatingAssignment, setIsCreatingAssignment] = useState(false); + const [userBalance, setUserBalance] = useState(0); const {stats} = useStats(); const {users, reload} = useUsers(); @@ -71,6 +72,14 @@ export default function CorporateDashboard({user}: Props) { setShowModal(!!selectedUser && page === ""); }, [selectedUser, page]); + useEffect(() => { + const relatedGroups = groups.filter((x) => x.name === "Students" || x.name === "Teachers" || x.name === "Corporate"); + const usersInGroups = relatedGroups.map((x) => x.participants).flat(); + const filteredCodes = codes.filter((x) => !x.userId || !usersInGroups.includes(x.userId)); + + setUserBalance(usersInGroups.length + filteredCodes.length); + }, [codes, groups]); + useEffect(() => { // in this case it fetches the master corporate account getUserCorporate(user.id).then(setCorporateUserToShow); @@ -350,7 +359,7 @@ export default function CorporateDashboard({user}: Props) { doc.data())); + res.status(200).json(snapshot.docs.map((doc) => doc.data())); } async function post(req: NextApiRequest, res: NextApiResponse) { - if (!req.session.user) { - res - .status(401) - .json({ ok: false, reason: "You must be logged in to generate a code!" }); - return; - } + if (!req.session.user) { + res.status(401).json({ok: false, reason: "You must be logged in to generate a code!"}); + return; + } - const { type, codes, infos, expiryDate } = req.body as { - type: Type; - codes: string[]; - infos?: { email: string; name: string; passport_id?: string }[]; - expiryDate: null | Date; - }; - const permission = PERMISSIONS.generateCode[type]; + const {type, codes, infos, expiryDate} = req.body as { + type: Type; + codes: string[]; + infos?: {email: string; name: string; passport_id?: string}[]; + expiryDate: null | Date; + }; + const permission = PERMISSIONS.generateCode[type]; - if (!permission.includes(req.session.user.type)) { - res.status(403).json({ - ok: false, - reason: - "Your account type does not have permissions to generate a code for that type of user!", - }); - return; - } + if (!permission.includes(req.session.user.type)) { + res.status(403).json({ + ok: false, + reason: "Your account type does not have permissions to generate a code for that type of user!", + }); + return; + } - const codesGeneratedByUserSnapshot = await getDocs( - query(collection(db, "codes"), where("creator", "==", req.session.user.id)), - ); - const userCodes = codesGeneratedByUserSnapshot.docs.map((x) => ({ - ...x.data(), - })); + const codesGeneratedByUserSnapshot = await getDocs(query(collection(db, "codes"), where("creator", "==", req.session.user.id))); + const creatorGroupsSnapshot = await getDocs(query(collection(db, "groups"), where("admin", "==", req.session.user.id))); - if (req.session.user.type === "corporate") { - const totalCodes = codesGeneratedByUserSnapshot.docs.length + codes.length; - const allowedCodes = - req.session.user.corporateInformation?.companyInformation.userAmount || 0; + const creatorGroups = ( + creatorGroupsSnapshot.docs.map((x) => ({ + ...x.data(), + })) as Group[] + ).filter((x) => x.name === "Students" || x.name === "Teachers" || x.name === "Corporate"); - if (totalCodes > allowedCodes) { - res.status(403).json({ - ok: false, - reason: `You have or would have exceeded your amount of allowed codes, you currently are allowed to generate ${ - allowedCodes - codesGeneratedByUserSnapshot.docs.length - } codes.`, - }); - return; - } - } + const usersInGroups = creatorGroups.flatMap((x) => x.participants); + const userCodes = codesGeneratedByUserSnapshot.docs.map((x) => ({ + ...x.data(), + })) as Code[]; - const codePromises = codes.map(async (code, index) => { - const codeRef = doc(db, "codes", code); - let codeInformation = { - type, - code, - creator: req.session.user!.id, - creationDate: new Date().toISOString(), - expiryDate, - }; + 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; - if (infos && infos.length > index) { - const { email, name, passport_id } = infos[index]; - const previousCode = userCodes.find((x) => x.email === email) as Code; + if (totalCodes > allowedCodes) { + res.status(403).json({ + ok: false, + reason: `You have or would have exceeded your amount of allowed codes, you currently are allowed to generate ${ + allowedCodes - codesGeneratedByUserSnapshot.docs.length + } codes.`, + }); + return; + } + } - const transport = prepareMailer(); - const mailOptions = prepareMailOptions( - { - type, - code: previousCode ? previousCode.code : code, - environment: process.env.ENVIRONMENT, - }, - [email.toLowerCase().trim()], - "EnCoach Registration", - "main", - ); + const codePromises = codes.map(async (code, index) => { + const codeRef = doc(db, "codes", code); + let codeInformation = { + type, + code, + creator: req.session.user!.id, + creationDate: new Date().toISOString(), + expiryDate, + }; - try { - await transport.sendMail(mailOptions); + if (infos && infos.length > index) { + const {email, name, passport_id} = infos[index]; + const previousCode = userCodes.find((x) => x.email === email) as Code; - if (!previousCode) { - await setDoc( - codeRef, - { - ...codeInformation, - email: email.trim().toLowerCase(), - name: name.trim(), - ...(passport_id ? { passport_id: passport_id.trim() } : {}), - }, - { merge: true }, - ); - } + const transport = prepareMailer(); + const mailOptions = prepareMailOptions( + { + type, + code: previousCode ? previousCode.code : code, + environment: process.env.ENVIRONMENT, + }, + [email.toLowerCase().trim()], + "EnCoach Registration", + "main", + ); - return true; - } catch (e) { - return false; - } - } else { - await setDoc(codeRef, codeInformation); - } - }); + try { + await transport.sendMail(mailOptions); - Promise.all(codePromises).then((results) => { - res.status(200).json({ ok: true, valid: results.filter((x) => x).length }); - }); + if (!previousCode) { + await setDoc( + codeRef, + { + ...codeInformation, + email: email.trim().toLowerCase(), + name: name.trim(), + ...(passport_id ? {passport_id: passport_id.trim()} : {}), + }, + {merge: true}, + ); + } + + return true; + } catch (e) { + return false; + } + } else { + await setDoc(codeRef, codeInformation); + } + }); + + Promise.all(codePromises).then((results) => { + res.status(200).json({ok: true, valid: results.filter((x) => x).length}); + }); } async function del(req: NextApiRequest, res: NextApiResponse) { - if (!req.session.user) { - res - .status(401) - .json({ ok: false, reason: "You must be logged in to generate a code!" }); - return; - } + if (!req.session.user) { + res.status(401).json({ok: false, reason: "You must be logged in to generate a code!"}); + return; + } - const codes = req.query.code as string[]; + const codes = req.query.code as string[]; - for (const code of codes) { - const snapshot = await getDoc(doc(db, "codes", code as string)); - if (!snapshot.exists()) continue; + for (const code of codes) { + const snapshot = await getDoc(doc(db, "codes", code as string)); + if (!snapshot.exists()) continue; - await deleteDoc(snapshot.ref); - } + await deleteDoc(snapshot.ref); + } - res.status(200).json({ codes }); + res.status(200).json({codes}); } From c6f35d77509bb8c91368ad2046b5a01875b6b886 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Thu, 15 Aug 2024 19:25:39 +0100 Subject: [PATCH 03/16] Enable the option to not have only full exams --- src/exams/Selection.tsx | 708 ++++++++++++++++------------------------ 1 file changed, 288 insertions(+), 420 deletions(-) diff --git a/src/exams/Selection.tsx b/src/exams/Selection.tsx index b33ea752..95169e27 100644 --- a/src/exams/Selection.tsx +++ b/src/exams/Selection.tsx @@ -1,442 +1,310 @@ /* eslint-disable @next/next/no-img-element */ -import { useState } from "react"; -import { Module } from "@/interfaces"; +import {useState} from "react"; +import {Module} from "@/interfaces"; import clsx from "clsx"; -import { User } from "@/interfaces/user"; +import {User} from "@/interfaces/user"; import ProgressBar from "@/components/Low/ProgressBar"; -import { - BsArrowRepeat, - BsBook, - BsCheck, - BsCheckCircle, - BsClipboard, - BsHeadphones, - BsMegaphone, - BsPen, - BsXCircle, -} from "react-icons/bs"; -import { totalExamsByModule } from "@/utils/stats"; +import {BsArrowRepeat, BsBook, BsCheck, BsCheckCircle, BsClipboard, BsHeadphones, BsMegaphone, BsPen, BsXCircle} from "react-icons/bs"; +import {totalExamsByModule} from "@/utils/stats"; import useStats from "@/hooks/useStats"; import Button from "@/components/Low/Button"; -import { calculateAverageLevel } from "@/utils/score"; -import { sortByModuleName } from "@/utils/moduleUtils"; -import { capitalize } from "lodash"; +import {calculateAverageLevel} from "@/utils/score"; +import {sortByModuleName} from "@/utils/moduleUtils"; +import {capitalize} from "lodash"; import ProfileSummary from "@/components/ProfileSummary"; -import { Variant } from "@/interfaces/exam"; -import useSessions, { Session } from "@/hooks/useSessions"; +import {Variant} from "@/interfaces/exam"; +import useSessions, {Session} from "@/hooks/useSessions"; import SessionCard from "@/components/Medium/SessionCard"; import useExamStore from "@/stores/examStore"; import moment from "moment"; interface Props { - user: User; - page: "exercises" | "exams"; - onStart: ( - modules: Module[], - avoidRepeated: boolean, - variant: Variant, - ) => void; - disableSelection?: boolean; + user: User; + page: "exercises" | "exams"; + onStart: (modules: Module[], avoidRepeated: boolean, variant: Variant) => void; + disableSelection?: boolean; } -export default function Selection({ - user, - page, - onStart, - disableSelection = false, -}: Props) { - const [selectedModules, setSelectedModules] = useState([]); - const [avoidRepeatedExams, setAvoidRepeatedExams] = useState(true); - const [variant, setVariant] = useState("full"); +export default function Selection({user, page, onStart, disableSelection = false}: Props) { + const [selectedModules, setSelectedModules] = useState([]); + const [avoidRepeatedExams, setAvoidRepeatedExams] = useState(true); + const [variant, setVariant] = useState("full"); - const { stats } = useStats(user?.id); - const { sessions, isLoading, reload } = useSessions(user.id); + const {stats} = useStats(user?.id); + const {sessions, isLoading, reload} = useSessions(user.id); - const state = useExamStore((state) => state); + const state = useExamStore((state) => state); - const toggleModule = (module: Module) => { - const modules = selectedModules.filter((x) => x !== module); - setSelectedModules((prev) => - prev.includes(module) ? modules : [...modules, module], - ); - }; + const toggleModule = (module: Module) => { + const modules = selectedModules.filter((x) => x !== module); + setSelectedModules((prev) => (prev.includes(module) ? modules : [...modules, module])); + }; - const loadSession = async (session: Session) => { - state.setSelectedModules(session.selectedModules); - state.setExam(session.exam); - state.setExams(session.exams); - state.setSessionId(session.sessionId); - state.setAssignment(session.assignment); - state.setExerciseIndex(session.exerciseIndex); - state.setPartIndex(session.partIndex); - state.setModuleIndex(session.moduleIndex); - state.setTimeSpent(session.timeSpent); - state.setUserSolutions(session.userSolutions); - state.setShowSolutions(false); - state.setQuestionIndex(session.questionIndex); - }; + const loadSession = async (session: Session) => { + state.setSelectedModules(session.selectedModules); + state.setExam(session.exam); + state.setExams(session.exams); + state.setSessionId(session.sessionId); + state.setAssignment(session.assignment); + state.setExerciseIndex(session.exerciseIndex); + state.setPartIndex(session.partIndex); + state.setModuleIndex(session.moduleIndex); + state.setTimeSpent(session.timeSpent); + state.setUserSolutions(session.userSolutions); + state.setShowSolutions(false); + state.setQuestionIndex(session.questionIndex); + }; - return ( - <> -
- {user && ( - - ), - label: "Reading", - value: totalExamsByModule(stats, "reading"), - tooltip: "The amount of reading exams performed.", - }, - { - icon: ( - - ), - label: "Listening", - value: totalExamsByModule(stats, "listening"), - tooltip: "The amount of listening exams performed.", - }, - { - icon: ( - - ), - label: "Writing", - value: totalExamsByModule(stats, "writing"), - tooltip: "The amount of writing exams performed.", - }, - { - icon: ( - - ), - label: "Speaking", - value: totalExamsByModule(stats, "speaking"), - tooltip: "The amount of speaking exams performed.", - }, - { - icon: ( - - ), - label: "Level", - value: totalExamsByModule(stats, "level"), - tooltip: "The amount of level exams performed.", - }, - ]} - /> - )} + return ( + <> +
+ {user && ( + , + label: "Reading", + value: totalExamsByModule(stats, "reading"), + tooltip: "The amount of reading exams performed.", + }, + { + icon: , + label: "Listening", + value: totalExamsByModule(stats, "listening"), + tooltip: "The amount of listening exams performed.", + }, + { + icon: , + label: "Writing", + value: totalExamsByModule(stats, "writing"), + tooltip: "The amount of writing exams performed.", + }, + { + icon: , + label: "Speaking", + value: totalExamsByModule(stats, "speaking"), + tooltip: "The amount of speaking exams performed.", + }, + { + icon: , + label: "Level", + value: totalExamsByModule(stats, "level"), + tooltip: "The amount of level exams performed.", + }, + ]} + /> + )} -
- About {capitalize(page)} - - {page === "exercises" && ( - <> - In the realm of language acquisition, practice makes perfect, - and our exercises are the key to unlocking your full potential. - Dive into a world of interactive and engaging exercises that - cater to diverse learning styles. From grammar drills that build - a strong foundation to vocabulary challenges that broaden your - lexicon, our exercises are carefully designed to make learning - English both enjoyable and effective. Whether you're - looking to reinforce specific skills or embark on a holistic - language journey, our exercises are your companions in the - pursuit of excellence. Embrace the joy of learning as you - navigate through a variety of activities that cater to every - facet of language acquisition. Your linguistic adventure starts - here! - - )} - {page === "exams" && ( - <> - Welcome to the heart of success on your English language - journey! Our exams are crafted with precision to assess and - enhance your language skills. Each test is a passport to your - linguistic prowess, designed to challenge and elevate your - abilities. Whether you're a beginner or a seasoned learner, - our exams cater to all levels, providing a comprehensive - evaluation of your reading, writing, speaking, and listening - skills. Prepare to embark on a journey of self-discovery and - language mastery as you navigate through our thoughtfully - curated exams. Your success is not just a destination; it's - a testament to your dedication and our commitment to empowering - you with the English language. - - )} - -
+
+ About {capitalize(page)} + + {page === "exercises" && ( + <> + In the realm of language acquisition, practice makes perfect, and our exercises are the key to unlocking your full + potential. Dive into a world of interactive and engaging exercises that cater to diverse learning styles. From grammar + drills that build a strong foundation to vocabulary challenges that broaden your lexicon, our exercises are carefully + designed to make learning English both enjoyable and effective. Whether you're looking to reinforce specific + skills or embark on a holistic language journey, our exercises are your companions in the pursuit of excellence. + Embrace the joy of learning as you navigate through a variety of activities that cater to every facet of language + acquisition. Your linguistic adventure starts here! + + )} + {page === "exams" && ( + <> + Welcome to the heart of success on your English language journey! Our exams are crafted with precision to assess and + enhance your language skills. Each test is a passport to your linguistic prowess, designed to challenge and elevate + your abilities. Whether you're a beginner or a seasoned learner, our exams cater to all levels, providing a + comprehensive evaluation of your reading, writing, speaking, and listening skills. Prepare to embark on a journey of + self-discovery and language mastery as you navigate through our thoughtfully curated exams. Your success is not just a + destination; it's a testament to your dedication and our commitment to empowering you with the English language. + + )} + +
- {sessions.length > 0 && ( -
-
-
- - Unfinished Sessions - - -
-
- - {sessions - .sort((a, b) => moment(b.date).diff(moment(a.date))) - .map((session) => ( - - ))} - -
- )} + {sessions.length > 0 && ( +
+
+
+ Unfinished Sessions + +
+
+ + {sessions + .sort((a, b) => moment(b.date).diff(moment(a.date))) + .map((session) => ( + + ))} + +
+ )} -
-
toggleModule("reading") - : undefined - } - className={clsx( - "bg-mti-white-alt relative flex w-64 max-w-xs cursor-pointer flex-col items-center gap-2 rounded-xl border p-5 pt-12 transition duration-300 ease-in-out", - selectedModules.includes("reading") || disableSelection - ? "border-mti-purple-light" - : "border-mti-gray-platinum", - )} - > -
- -
- Reading: -

- Expand your vocabulary, improve your reading comprehension and - improve your ability to interpret texts in English. -

- {!selectedModules.includes("reading") && - !selectedModules.includes("level") && - !disableSelection && ( -
- )} - {(selectedModules.includes("reading") || disableSelection) && ( - - )} - {selectedModules.includes("level") && ( - - )} -
-
toggleModule("listening") - : undefined - } - className={clsx( - "bg-mti-white-alt relative flex w-64 max-w-xs cursor-pointer flex-col items-center gap-2 rounded-xl border p-5 pt-12 transition duration-300 ease-in-out", - selectedModules.includes("listening") || disableSelection - ? "border-mti-purple-light" - : "border-mti-gray-platinum", - )} - > -
- -
- Listening: -

- Improve your ability to follow conversations in English and your - ability to understand different accents and intonations. -

- {!selectedModules.includes("listening") && - !selectedModules.includes("level") && - !disableSelection && ( -
- )} - {(selectedModules.includes("listening") || disableSelection) && ( - - )} - {selectedModules.includes("level") && ( - - )} -
-
toggleModule("writing") - : undefined - } - className={clsx( - "bg-mti-white-alt relative flex w-64 max-w-xs cursor-pointer flex-col items-center gap-2 rounded-xl border p-5 pt-12 transition duration-300 ease-in-out", - selectedModules.includes("writing") || disableSelection - ? "border-mti-purple-light" - : "border-mti-gray-platinum", - )} - > -
- -
- Writing: -

- Allow you to practice writing in a variety of formats, from simple - paragraphs to complex essays. -

- {!selectedModules.includes("writing") && - !selectedModules.includes("level") && - !disableSelection && ( -
- )} - {(selectedModules.includes("writing") || disableSelection) && ( - - )} - {selectedModules.includes("level") && ( - - )} -
-
toggleModule("speaking") - : undefined - } - className={clsx( - "bg-mti-white-alt relative flex w-64 max-w-xs cursor-pointer flex-col items-center gap-2 rounded-xl border p-5 pt-12 transition duration-300 ease-in-out", - selectedModules.includes("speaking") || disableSelection - ? "border-mti-purple-light" - : "border-mti-gray-platinum", - )} - > -
- -
- Speaking: -

- You'll have access to interactive dialogs, pronunciation - exercises and speech recordings. -

- {!selectedModules.includes("speaking") && - !selectedModules.includes("level") && - !disableSelection && ( -
- )} - {(selectedModules.includes("speaking") || disableSelection) && ( - - )} - {selectedModules.includes("level") && ( - - )} -
- {!disableSelection && ( -
toggleModule("level") - : undefined - } - className={clsx( - "bg-mti-white-alt relative flex w-64 max-w-xs cursor-pointer flex-col items-center gap-2 rounded-xl border p-5 pt-12 transition duration-300 ease-in-out", - selectedModules.includes("level") || disableSelection - ? "border-mti-purple-light" - : "border-mti-gray-platinum", - )} - > -
- -
- Level: -

- You'll be able to test your english level with multiple - choice questions. -

- {!selectedModules.includes("level") && - selectedModules.length === 0 && - !disableSelection && ( -
- )} - {(selectedModules.includes("level") || disableSelection) && ( - - )} - {!selectedModules.includes("level") && - selectedModules.length > 0 && ( - - )} -
- )} -
-
-
-
setAvoidRepeatedExams((prev) => !prev)} - > - -
- -
- - Avoid Repeated Questions - -
-
setVariant((prev) => (prev === "full" ? "partial" : "full"))}> - > - -
- -
- Full length exams -
-
-
- -
- -
-
- - ); +
+
toggleModule("reading") : undefined} + className={clsx( + "bg-mti-white-alt relative flex w-64 max-w-xs cursor-pointer flex-col items-center gap-2 rounded-xl border p-5 pt-12 transition duration-300 ease-in-out", + selectedModules.includes("reading") || disableSelection ? "border-mti-purple-light" : "border-mti-gray-platinum", + )}> +
+ +
+ Reading: +

+ Expand your vocabulary, improve your reading comprehension and improve your ability to interpret texts in English. +

+ {!selectedModules.includes("reading") && !selectedModules.includes("level") && !disableSelection && ( +
+ )} + {(selectedModules.includes("reading") || disableSelection) && ( + + )} + {selectedModules.includes("level") && } +
+
toggleModule("listening") : undefined} + className={clsx( + "bg-mti-white-alt relative flex w-64 max-w-xs cursor-pointer flex-col items-center gap-2 rounded-xl border p-5 pt-12 transition duration-300 ease-in-out", + selectedModules.includes("listening") || disableSelection ? "border-mti-purple-light" : "border-mti-gray-platinum", + )}> +
+ +
+ Listening: +

+ Improve your ability to follow conversations in English and your ability to understand different accents and intonations. +

+ {!selectedModules.includes("listening") && !selectedModules.includes("level") && !disableSelection && ( +
+ )} + {(selectedModules.includes("listening") || disableSelection) && ( + + )} + {selectedModules.includes("level") && } +
+
toggleModule("writing") : undefined} + className={clsx( + "bg-mti-white-alt relative flex w-64 max-w-xs cursor-pointer flex-col items-center gap-2 rounded-xl border p-5 pt-12 transition duration-300 ease-in-out", + selectedModules.includes("writing") || disableSelection ? "border-mti-purple-light" : "border-mti-gray-platinum", + )}> +
+ +
+ Writing: +

+ Allow you to practice writing in a variety of formats, from simple paragraphs to complex essays. +

+ {!selectedModules.includes("writing") && !selectedModules.includes("level") && !disableSelection && ( +
+ )} + {(selectedModules.includes("writing") || disableSelection) && ( + + )} + {selectedModules.includes("level") && } +
+
toggleModule("speaking") : undefined} + className={clsx( + "bg-mti-white-alt relative flex w-64 max-w-xs cursor-pointer flex-col items-center gap-2 rounded-xl border p-5 pt-12 transition duration-300 ease-in-out", + selectedModules.includes("speaking") || disableSelection ? "border-mti-purple-light" : "border-mti-gray-platinum", + )}> +
+ +
+ Speaking: +

+ You'll have access to interactive dialogs, pronunciation exercises and speech recordings. +

+ {!selectedModules.includes("speaking") && !selectedModules.includes("level") && !disableSelection && ( +
+ )} + {(selectedModules.includes("speaking") || disableSelection) && ( + + )} + {selectedModules.includes("level") && } +
+ {!disableSelection && ( +
toggleModule("level") : undefined} + className={clsx( + "bg-mti-white-alt relative flex w-64 max-w-xs cursor-pointer flex-col items-center gap-2 rounded-xl border p-5 pt-12 transition duration-300 ease-in-out", + selectedModules.includes("level") || disableSelection ? "border-mti-purple-light" : "border-mti-gray-platinum", + )}> +
+ +
+ Level: +

You'll be able to test your english level with multiple choice questions.

+ {!selectedModules.includes("level") && selectedModules.length === 0 && !disableSelection && ( +
+ )} + {(selectedModules.includes("level") || disableSelection) && ( + + )} + {!selectedModules.includes("level") && selectedModules.length > 0 && ( + + )} +
+ )} +
+
+
+
setAvoidRepeatedExams((prev) => !prev)}> + +
+ +
+ + Avoid Repeated Questions + +
+
setVariant((prev) => (prev === "full" ? "partial" : "full"))}> + +
+ +
+ Full length exams +
+
+
+ +
+ +
+
+ + ); } From 38e48c90bb8e3ef0909e79f3e7703044245975bb Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Thu, 15 Aug 2024 23:37:34 +0100 Subject: [PATCH 04/16] Updated the label for Admin --- src/resources/user.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/user.ts b/src/resources/user.ts index c57c1ee4..e0aa0591 100644 --- a/src/resources/user.ts +++ b/src/resources/user.ts @@ -5,9 +5,9 @@ export const USER_TYPE_LABELS: {[key in Type]: string} = { teacher: "Teacher", corporate: "Corporate", agent: "Country Manager", - admin: "Admin", + admin: "Super Admin", developer: "Developer", - mastercorporate: "Master Corporate" + mastercorporate: "Master Corporate", }; export function isCorporateUser(user: User): user is CorporateUser { From 0a28c2bd41442e48ef7405d56427803d96ed63a7 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Thu, 15 Aug 2024 23:55:08 +0100 Subject: [PATCH 05/16] Added a "last login" to the users --- src/interfaces/user.ts | 244 ++++++++++++--------------- src/pages/(admin)/Lists/UserList.tsx | 40 +++-- src/pages/api/user.ts | 2 + 3 files changed, 139 insertions(+), 147 deletions(-) diff --git a/src/interfaces/user.ts b/src/interfaces/user.ts index ddda1ccb..c704e4b7 100644 --- a/src/interfaces/user.ts +++ b/src/interfaces/user.ts @@ -1,189 +1,161 @@ -import { Module } from "."; -import { InstructorGender } from "./exam"; -import { PermissionType } from "./permissions"; +import {Module} from "."; +import {InstructorGender} from "./exam"; +import {PermissionType} from "./permissions"; -export type User = - | StudentUser - | TeacherUser - | CorporateUser - | AgentUser - | AdminUser - | DeveloperUser - | MasterCorporateUser; +export type User = StudentUser | TeacherUser | CorporateUser | AgentUser | AdminUser | DeveloperUser | MasterCorporateUser; export type UserStatus = "active" | "disabled" | "paymentDue"; export interface BasicUser { - email: string; - name: string; - profilePicture: string; - id: string; - isFirstLogin: boolean; - focus: "academic" | "general"; - levels: { [key in Module]: number }; - desiredLevels: { [key in Module]: number }; - type: Type; - bio: string; - isVerified: boolean; - subscriptionExpirationDate?: null | Date; - registrationDate?: Date; - status: UserStatus; - permissions: PermissionType[], + email: string; + name: string; + profilePicture: string; + id: string; + isFirstLogin: boolean; + focus: "academic" | "general"; + levels: {[key in Module]: number}; + desiredLevels: {[key in Module]: number}; + type: Type; + bio: string; + isVerified: boolean; + subscriptionExpirationDate?: null | Date; + registrationDate?: Date; + status: UserStatus; + permissions: PermissionType[]; + lastLogin?: Date; } export interface StudentUser extends BasicUser { - type: "student"; - preferredGender?: InstructorGender; - demographicInformation?: DemographicInformation; - preferredTopics?: string[]; + type: "student"; + preferredGender?: InstructorGender; + demographicInformation?: DemographicInformation; + preferredTopics?: string[]; } export interface TeacherUser extends BasicUser { - type: "teacher"; - demographicInformation?: DemographicInformation; + type: "teacher"; + demographicInformation?: DemographicInformation; } export interface CorporateUser extends BasicUser { - type: "corporate"; - corporateInformation: CorporateInformation; - demographicInformation?: DemographicCorporateInformation; + type: "corporate"; + corporateInformation: CorporateInformation; + demographicInformation?: DemographicCorporateInformation; } export interface MasterCorporateUser extends BasicUser { - type: "mastercorporate"; - corporateInformation: CorporateInformation; - demographicInformation?: DemographicCorporateInformation; + type: "mastercorporate"; + corporateInformation: CorporateInformation; + demographicInformation?: DemographicCorporateInformation; } export interface AgentUser extends BasicUser { - type: "agent"; - agentInformation: AgentInformation; - demographicInformation?: DemographicInformation; + type: "agent"; + agentInformation: AgentInformation; + demographicInformation?: DemographicInformation; } export interface AdminUser extends BasicUser { - type: "admin"; - demographicInformation?: DemographicInformation; + type: "admin"; + demographicInformation?: DemographicInformation; } export interface DeveloperUser extends BasicUser { - type: "developer"; - preferredGender?: InstructorGender; - demographicInformation?: DemographicInformation; - preferredTopics?: string[]; + type: "developer"; + preferredGender?: InstructorGender; + demographicInformation?: DemographicInformation; + preferredTopics?: string[]; } export interface CorporateInformation { - companyInformation: CompanyInformation; - monthlyDuration: number; - payment?: { - value: number; - currency: string; - commission: number; - }; - referralAgent?: string; + companyInformation: CompanyInformation; + monthlyDuration: number; + payment?: { + value: number; + currency: string; + commission: number; + }; + referralAgent?: string; } export interface AgentInformation { - companyName: string; - commercialRegistration: string; - companyArabName?: string; + companyName: string; + commercialRegistration: string; + companyArabName?: string; } export interface CompanyInformation { - name: string; - userAmount: number; + name: string; + userAmount: number; } export interface DemographicInformation { - country: string; - phone: string; - gender: Gender; - employment: EmploymentStatus; - passport_id?: string; - timezone?: string; + country: string; + phone: string; + gender: Gender; + employment: EmploymentStatus; + passport_id?: string; + timezone?: string; } export interface DemographicCorporateInformation { - country: string; - phone: string; - gender: Gender; - position: string; - timezone?: string; + country: string; + phone: string; + gender: Gender; + position: string; + timezone?: string; } export type Gender = "male" | "female" | "other"; -export type EmploymentStatus = - | "employed" - | "student" - | "self-employed" - | "unemployed" - | "retired" - | "other"; -export const EMPLOYMENT_STATUS: { status: EmploymentStatus; label: string }[] = - [ - { status: "student", label: "Student" }, - { status: "employed", label: "Employed" }, - { status: "unemployed", label: "Unemployed" }, - { status: "self-employed", label: "Self-employed" }, - { status: "retired", label: "Retired" }, - { status: "other", label: "Other" }, - ]; +export type EmploymentStatus = "employed" | "student" | "self-employed" | "unemployed" | "retired" | "other"; +export const EMPLOYMENT_STATUS: {status: EmploymentStatus; label: string}[] = [ + {status: "student", label: "Student"}, + {status: "employed", label: "Employed"}, + {status: "unemployed", label: "Unemployed"}, + {status: "self-employed", label: "Self-employed"}, + {status: "retired", label: "Retired"}, + {status: "other", label: "Other"}, +]; export interface Stat { - id: string; - user: string; - exam: string; - exercise: string; - session: string; - date: number; - module: Module; - solutions: any[]; - type: string; - timeSpent?: number; - inactivity?: number; - assignment?: string; - score: { - correct: number; - total: number; - missing: number; - }; - isDisabled?: boolean; + id: string; + user: string; + exam: string; + exercise: string; + session: string; + date: number; + module: Module; + solutions: any[]; + type: string; + timeSpent?: number; + inactivity?: number; + assignment?: string; + score: { + correct: number; + total: number; + missing: number; + }; + isDisabled?: boolean; } export interface Group { - admin: string; - name: string; - participants: string[]; - id: string; - disableEditing?: boolean; + admin: string; + name: string; + participants: string[]; + id: string; + disableEditing?: boolean; } export interface Code { - code: string; - creator: string; - expiryDate: Date; - type: Type; - creationDate?: string; - userId?: string; - email?: string; - name?: string; - passport_id?: string; + code: string; + creator: string; + expiryDate: Date; + type: Type; + creationDate?: string; + userId?: string; + email?: string; + name?: string; + passport_id?: string; } -export type Type = - | "student" - | "teacher" - | "corporate" - | "admin" - | "developer" - | "agent" - | "mastercorporate"; -export const userTypes: Type[] = [ - "student", - "teacher", - "corporate", - "admin", - "developer", - "agent", - "mastercorporate", -]; +export type Type = "student" | "teacher" | "corporate" | "admin" | "developer" | "agent" | "mastercorporate"; +export const userTypes: Type[] = ["student", "teacher", "corporate", "admin", "developer", "agent", "mastercorporate"]; diff --git a/src/pages/(admin)/Lists/UserList.tsx b/src/pages/(admin)/Lists/UserList.tsx index cd939522..fb66d7ae 100644 --- a/src/pages/(admin)/Lists/UserList.tsx +++ b/src/pages/(admin)/Lists/UserList.tsx @@ -279,10 +279,10 @@ export default function UserList({ ) as any, cell: (info) => info.getValue() - ? `${countryCodes.findOne("countryCode" as any, info.getValue()).flag} ${ - countries[info.getValue() as unknown as keyof TCountries].name - } (+${countryCodes.findOne("countryCode" as any, info.getValue()).countryCallingCode})` - : "Not available", + ? `${countryCodes.findOne("countryCode" as any, info.getValue())?.flag} ${ + countries[info.getValue() as unknown as keyof TCountries]?.name + } (+${countryCodes.findOne("countryCode" as any, info.getValue())?.countryCallingCode})` + : "N/A", }), columnHelper.accessor("demographicInformation.phone", { header: ( @@ -291,7 +291,7 @@ export default function UserList({ ) as any, - cell: (info) => info.getValue() || "Not available", + cell: (info) => info.getValue() || "N/A", enableSorting: true, }), columnHelper.accessor( @@ -301,14 +301,23 @@ export default function UserList({ id: "employment", header: ( ) as any, - cell: (info) => (info.row.original.type === "corporate" ? info.getValue() : capitalize(info.getValue())) || "Not available", + cell: (info) => (info.row.original.type === "corporate" ? info.getValue() : capitalize(info.getValue())) || "N/A", enableSorting: true, }, ), + columnHelper.accessor("lastLogin", { + header: ( + + ) as any, + cell: (info) => (!!info.getValue() ? moment(info.getValue()).format("YYYY-MM-DD HH:mm") : "N/A"), + }), columnHelper.accessor("demographicInformation.gender", { header: ( ) as any, - cell: (info) => capitalize(info.getValue()) || "Not available", + cell: (info) => capitalize(info.getValue()) || "N/A", enableSorting: true, }), { @@ -379,7 +388,7 @@ export default function UserList({ columnHelper.accessor("corporateInformation.companyInformation.name", { header: ( ) as any, @@ -388,7 +397,7 @@ export default function UserList({ columnHelper.accessor("subscriptionExpirationDate", { header: ( ) as any, @@ -401,7 +410,7 @@ export default function UserList({ columnHelper.accessor("isVerified", { header: ( ) as any, @@ -464,6 +473,15 @@ export default function UserList({ return 0; } + if (sorter === "lastLogin" || sorter === reverseString("lastLogin")) { + if (!a.lastLogin && b.lastLogin) return sorter === "lastLogin" ? -1 : 1; + if (a.lastLogin && !b.lastLogin) return sorter === "lastLogin" ? 1 : -1; + if (!a.lastLogin && !b.lastLogin) return 0; + if (moment(a.lastLogin).isAfter(b.lastLogin)) return sorter === "lastLogin" ? -1 : 1; + if (moment(b.lastLogin).isAfter(a.lastLogin)) return sorter === "lastLogin" ? 1 : -1; + return 0; + } + if (sorter === "country" || sorter === reverseString("country")) { if (!a.demographicInformation?.country && b.demographicInformation?.country) return sorter === "country" ? -1 : 1; if (a.demographicInformation?.country && !b.demographicInformation?.country) return sorter === "country" ? 1 : -1; diff --git a/src/pages/api/user.ts b/src/pages/api/user.ts index 6d9c176b..6240aa18 100644 --- a/src/pages/api/user.ts +++ b/src/pages/api/user.ts @@ -107,10 +107,12 @@ async function get(req: NextApiRequest, res: NextApiResponse) { } const user = docUser.data() as User; + await setDoc(docUser.ref, {lastLogin: new Date().toISOString()}, {merge: true}); req.session.user = { ...user, id: req.session.user.id, + lastLogin: new Date(), }; await req.session.save(); From 878c7c2ef046e96e32c444db1817cd8e4197af88 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Fri, 16 Aug 2024 11:50:27 +0100 Subject: [PATCH 06/16] Updated the Groups List to allow teachers to view their corporate's students --- src/hooks/useGroups.tsx | 20 +++++++++++--------- src/hooks/usePermissions.tsx | 1 - src/pages/(admin)/BatchCreateUser.tsx | 1 - src/pages/(admin)/Lists/GroupList.tsx | 6 +++++- src/pages/settings.tsx | 2 +- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/hooks/useGroups.tsx b/src/hooks/useGroups.tsx index a4d39dc7..9b4db75a 100644 --- a/src/hooks/useGroups.tsx +++ b/src/hooks/useGroups.tsx @@ -2,32 +2,34 @@ import {Group, User} from "@/interfaces/user"; import axios from "axios"; import {useEffect, useState} from "react"; -export default function useGroups(admin?: string, userType?: string) { +export default function useGroups(admin?: string, userType?: string, teacher?: string) { const [groups, setGroups] = useState([]); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); - const isMasterType = userType?.startsWith('master'); + const isMasterType = userType?.startsWith("master"); const getData = () => { setIsLoading(true); - const url = admin ? `/api/groups?admin=${admin}` : "/api/groups"; + const url = admin && !teacher ? `/api/groups?admin=${admin}` : "/api/groups"; axios .get(url) .then((response) => { - if(isMasterType) { - return setGroups(response.data); - } - const filter = (g: Group) => g.admin === admin || g.participants.includes(admin || ""); + if (isMasterType) return setGroups(response.data); - const filteredGroups = admin ? response.data.filter(filter) : response.data; + const filterByAdmins = !!teacher + ? [teacher, ...response.data.filter((g) => g.participants.includes(teacher)).flatMap((g) => g.admin)] + : [admin]; + const filter = (g: Group) => filterByAdmins.includes(g.admin) || g.participants.includes(admin || ""); + + const filteredGroups = !!admin || !!teacher ? response.data.filter(filter) : response.data; return setGroups(admin ? filteredGroups.map((g) => ({...g, disableEditing: g.disableEditing || g.admin !== admin})) : filteredGroups); }) .finally(() => setIsLoading(false)); }; - useEffect(getData, [admin, isMasterType]); + useEffect(getData, [admin, teacher, isMasterType]); return {groups, isLoading, isError, reload: getData}; } diff --git a/src/hooks/usePermissions.tsx b/src/hooks/usePermissions.tsx index 67d49394..5fdf962e 100644 --- a/src/hooks/usePermissions.tsx +++ b/src/hooks/usePermissions.tsx @@ -17,7 +17,6 @@ export default function usePermissions(user: string) { const permissionTypes = response.data .filter((x) => !x.users.includes(user)) .reduce((acc, curr) => [...acc, curr.type], [] as PermissionType[]); - console.log(response.data, permissionTypes); setPermissions(permissionTypes); }) .finally(() => setIsLoading(false)); diff --git a/src/pages/(admin)/BatchCreateUser.tsx b/src/pages/(admin)/BatchCreateUser.tsx index 43bd8ec8..d0d50c57 100644 --- a/src/pages/(admin)/BatchCreateUser.tsx +++ b/src/pages/(admin)/BatchCreateUser.tsx @@ -111,7 +111,6 @@ export default function BatchCreateUser({user}: {user: User}) { return clear(); } - console.log(information); setInfos(information); } catch { toast.error( diff --git a/src/pages/(admin)/Lists/GroupList.tsx b/src/pages/(admin)/Lists/GroupList.tsx index 4db19d47..3ac5be76 100644 --- a/src/pages/(admin)/Lists/GroupList.tsx +++ b/src/pages/(admin)/Lists/GroupList.tsx @@ -201,7 +201,11 @@ export default function GroupList({user}: {user: User}) { const {permissions} = usePermissions(user?.id || ""); const {users} = useUsers(); - const {groups, reload} = useGroups(user && filterTypes.includes(user?.type) ? user.id : undefined, user?.type); + const {groups, reload} = useGroups( + user && filterTypes.includes(user?.type) ? user.id : undefined, + user?.type, + user?.type === "teacher" ? user?.id : undefined, + ); useEffect(() => { if (user && ["corporate", "teacher", "mastercorporate"].includes(user.type)) { diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index a744b2a0..80c08fbb 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -28,7 +28,7 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { }; } - if (shouldRedirectHome(user) || !["developer", "admin", "corporate", "agent", "mastercorporate"].includes(user.type)) { + if (shouldRedirectHome(user) || !checkAccess(user, ["admin", "developer", "corporate", "teacher", "mastercorporate"])) { return { redirect: { destination: "/", From f0ff6ac691151161fa6be29e18b52d91d898e0d3 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Sat, 17 Aug 2024 19:15:20 +0100 Subject: [PATCH 07/16] ENCOA-87: Allow MasterCorporate & Corporate to change the type of students and teachers --- src/components/UserCard.tsx | 34 +++++++++++++++++++++++++--- src/pages/(admin)/Lists/UserList.tsx | 14 ++++++++---- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/components/UserCard.tsx b/src/components/UserCard.tsx index 23a79fea..60c8aa79 100644 --- a/src/components/UserCard.tsx +++ b/src/components/UserCard.tsx @@ -525,7 +525,9 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers, setExpiryDate(checked ? user.subscriptionExpirationDate || new Date() : null)} - disabled={disabled}> + disabled={ + disabled || (!["admin", "developer"].includes(loggedInUser.type) && !!loggedInUser.subscriptionExpirationDate) + }> Enabled @@ -564,7 +566,12 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers, - {checkAccess(loggedInUser, ["developer", "admin"]) && ( + {checkAccess( + loggedInUser, + ["developer", "admin", "corporate", "mastercorporate"], + permissions, + user.type === "teacher" ? "editTeacher" : user.type === "student" ? "editStudent" : undefined, + ) && ( <>
@@ -600,7 +607,28 @@ const UserCard = ({user, loggedInUser, onClose, onViewStudents, onViewTeachers, ({ - value: x.id, - label: `${x.name} - ${x.email}`, - }))} - value={corporate ? { value: corporate.id, label: corporate.name } : null} - onChange={(value) => setCorporateId(value?.value!)} - styles={{ - menuPortal: (base) => ({ ...base, zIndex: 9999 }), - option: (styles, state) => ({ - ...styles, - backgroundColor: state.isFocused - ? "#D5D9F0" - : state.isSelected - ? "#7872BF" - : "white", - color: state.isFocused ? "black" : styles.color, - }), - }} - /> - {groupsParticipants.map((u) => ( - - ))} - - ); + return ( + <> + setName(e)} - placeholder="Enter your name" - defaultValue={name} - required - /> - ) : ( - - setCorporateInformation((prev) => ({ - ...prev!, - companyInformation: { - ...prev!.companyInformation, - name: e, - }, - })) - } - placeholder="Enter your company's name" - defaultValue={corporateInformation?.companyInformation.name} - required - /> - )} + return ( + +
+

Edit Profile

+
+
+

Edit Profile

+
e.preventDefault()}> + + {user.type !== "corporate" ? ( + setName(e)} + placeholder="Enter your name" + defaultValue={name} + required + /> + ) : ( + + setCorporateInformation((prev) => ({ + ...prev!, + companyInformation: { + ...prev!.companyInformation, + name: e, + }, + })) + } + placeholder="Enter your company's name" + defaultValue={corporateInformation?.companyInformation.name} + required + /> + )} - {user.type === "agent" && ( - setArabName(e)} - placeholder="Enter your arab name" - defaultValue={arabName} - required - /> - )} + {user.type === "agent" && ( + setArabName(e)} + placeholder="Enter your arab name" + defaultValue={arabName} + required + /> + )} - setEmail(e)} - placeholder="Enter email address" - defaultValue={email} - required - /> - - - setPassword(e)} - placeholder="Enter your password" - required - /> - setNewPassword(e)} - placeholder="Enter your new password (optional)" - /> - - {user.type === "agent" && ( -
- null} - placeholder="Enter your company's name" - defaultValue={companyName} - disabled - /> - null} - placeholder="Enter commercial registration" - defaultValue={commercialRegistration} - disabled - /> -
- )} + setEmail(e)} + placeholder="Enter email address" + defaultValue={email} + required + /> + + + setPassword(e)} + placeholder="Enter your password" + required + /> + setNewPassword(e)} + placeholder="Enter your new password (optional)" + /> + + {user.type === "agent" && ( +
+ null} + placeholder="Enter your company's name" + defaultValue={companyName} + disabled + /> + null} + placeholder="Enter commercial registration" + defaultValue={commercialRegistration} + disabled + /> +
+ )} - -
- - -
- setPhone(e)} - placeholder="Enter phone number" - defaultValue={phone} - required - /> -
+ +
+ + +
+ setPhone(e)} + placeholder="Enter phone number" + defaultValue={phone} + required + /> +
- {user.type === "student" ? ( - - setPassportID(e)} - placeholder="Enter National ID or Passport number" - value={passport_id} - required - /> - - - ) : ( - - )} + {user.type === "student" ? ( + + setPassportID(e)} + placeholder="Enter National ID or Passport number" + value={passport_id} + required + /> + + + ) : ( + + )} - + - {desiredLevels && - ["developer", "student"].includes(user.type) && ( - <> -
- - - > - } - /> -
-
- -
- - -
-
- - )} + {desiredLevels && ["developer", "student"].includes(user.type) && ( + <> +
+ + >} + /> +
+
+ +
+ + +
+
+ + )} - {preferredGender && - ["developer", "student"].includes(user.type) && ( - <> - - -
- - (value ? setPreferredGender(value.value as InstructorGender) : null)} + options={[ + {value: "male", label: "Male"}, + {value: "female", label: "Female"}, + {value: "varied", label: "Varied"}, + ]} + /> +
+
+ + +
+
- setIsPreferredTopicsOpen(false)} - selectTopics={setPreferredTopics} - initialTopics={preferredTopics || []} - /> + setIsPreferredTopicsOpen(false)} + selectTopics={setPreferredTopics} + initialTopics={preferredTopics || []} + /> - - - )} + + + )} - {user.type === "corporate" && ( - <> - - null} - label="Number of users" - defaultValue={ - user.corporateInformation.companyInformation.userAmount - } - disabled - required - /> - null} - label="Pricing" - defaultValue={`${user.corporateInformation.payment?.value} ${user.corporateInformation.payment?.currency}`} - disabled - required - /> - - - - )} + {user.type === "corporate" && ( + <> + + null} + label="Number of users" + defaultValue={user.corporateInformation.companyInformation.userAmount} + disabled + required + /> + null} + label="Pricing" + defaultValue={`${user.corporateInformation.payment?.value} ${user.corporateInformation.payment?.currency}`} + disabled + required + /> + + + + )} - {user.type === "corporate" && ( - <> - - - setName(e)} - placeholder="Enter your name" - defaultValue={name} - required - /> - - - - )} + {user.type === "corporate" && ( + <> + + + setName(e)} + placeholder="Enter your name" + defaultValue={name} + required + /> + + + + )} - {user.type === "corporate" && - user.corporateInformation.referralAgent && ( - <> - - - null} - defaultValue={ - users.find( - (x) => - x.id === user.corporateInformation.referralAgent - )?.name - } - type="text" - label="Country Manager's Name" - placeholder="Not available" - required - disabled - /> - null} - defaultValue={ - users.find( - (x) => - x.id === user.corporateInformation.referralAgent - )?.email - } - type="text" - label="Country Manager's E-mail" - placeholder="Not available" - required - disabled - /> - - -
- - - x.id === user.corporateInformation.referralAgent - )?.demographicInformation?.country - } - onChange={() => null} - disabled - /> -
+ {user.type === "corporate" && user.corporateInformation.referralAgent && ( + <> + + + null} + defaultValue={users.find((x) => x.id === user.corporateInformation.referralAgent)?.name} + type="text" + label="Country Manager's Name" + placeholder="Not available" + required + disabled + /> + null} + defaultValue={users.find((x) => x.id === user.corporateInformation.referralAgent)?.email} + type="text" + label="Country Manager's E-mail" + placeholder="Not available" + required + disabled + /> + + +
+ + x.id === user.corporateInformation.referralAgent)?.demographicInformation + ?.country + } + onChange={() => null} + disabled + /> +
- null} - placeholder="Not available" - defaultValue={ - users.find( - (x) => - x.id === user.corporateInformation.referralAgent - )?.demographicInformation?.phone - } - disabled - required - /> -
- - )} + null} + placeholder="Not available" + defaultValue={ + users.find((x) => x.id === user.corporateInformation.referralAgent)?.demographicInformation?.phone + } + disabled + required + /> +
+ + )} - {user.type !== "corporate" && ( - - + {user.type !== "corporate" && ( + + -
- - -
-
- )} - -
-
-
(profilePictureInput.current as any)?.click()} - > -
-
- -
- {user.name} -
- - (profilePictureInput.current as any)?.click()} - className="cursor-pointer text-mti-purple-light text-sm" - > - Change picture - -
- {USER_TYPE_LABELS[user.type]} -
-
- {user.type === "agent" && ( -
- { -
- )} - {manualDownloadLink && ( - - - - )} -
-
-
- Bio -