From d2276eba1d8796600acf4f6f0b11e13cc8343ee9 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Tue, 2 Jan 2024 11:07:18 +0000 Subject: [PATCH] Made it so that students, connected to a corporate, if they change their e-mail, they get unassigned --- src/components/Diagnostic.tsx | 6 ++-- src/dashboards/Student.tsx | 3 +- src/pages/api/users/update.ts | 41 ++++++++++++++++-------- src/pages/profile.tsx | 60 ++++++++++++++++++++++------------- 4 files changed, 70 insertions(+), 40 deletions(-) diff --git a/src/components/Diagnostic.tsx b/src/components/Diagnostic.tsx index 943d90e7..a221f1d2 100644 --- a/src/components/Diagnostic.tsx +++ b/src/components/Diagnostic.tsx @@ -22,8 +22,8 @@ interface Props { export default function Diagnostic({onFinish}: Props) { const [focus, setFocus] = useState<"academic" | "general">(); - const [levels, setLevels] = useState({reading: -1, listening: -1, writing: -1, speaking: -1}); - const [desiredLevels, setDesiredLevels] = useState({reading: 9, listening: 9, writing: 9, speaking: 9}); + const [levels, setLevels] = useState({reading: -1, listening: -1, writing: -1, speaking: -1, level: 0}); + const [desiredLevels, setDesiredLevels] = useState({reading: 9, listening: 9, writing: 9, speaking: 9, level: 9}); const router = useRouter(); @@ -51,7 +51,7 @@ export default function Diagnostic({onFinish}: Props) { axios .patch("/api/users/update", { focus, - levels: Object.values(levels).includes(-1) ? {reading: 0, listening: 0, writing: 0, speaking: 0} : levels, + levels: Object.values(levels).includes(-1) ? {reading: 0, listening: 0, writing: 0, speaking: 0, level: 0} : levels, desiredLevels, isFirstLogin: false, }) diff --git a/src/dashboards/Student.tsx b/src/dashboards/Student.tsx index ec67cff7..369249f7 100644 --- a/src/dashboards/Student.tsx +++ b/src/dashboards/Student.tsx @@ -191,11 +191,12 @@ export default function StudentDashboard({user}: Props) { {module === "listening" && } {module === "writing" && } {module === "speaking" && } + {module === "level" && }
{capitalize(module)} - Level {user.levels[module]} / Level {user.desiredLevels[module]} + Level {user.levels[module] || 0} / Level {user.desiredLevels[module] || 9}
diff --git a/src/pages/api/users/update.ts b/src/pages/api/users/update.ts index 7b6ea5f3..e82fdf58 100644 --- a/src/pages/api/users/update.ts +++ b/src/pages/api/users/update.ts @@ -4,14 +4,14 @@ import {app, storage} from "@/firebase"; import {getFirestore, collection, getDocs, getDoc, doc, setDoc, query, where} from "firebase/firestore"; import {withIronSessionApiRoute} from "iron-session/next"; import {sessionOptions} from "@/lib/session"; -import {User} from "@/interfaces/user"; +import {Group, User} from "@/interfaces/user"; import {getDownloadURL, getStorage, ref, uploadBytes} from "firebase/storage"; import {getAuth, signInWithEmailAndPassword, updateEmail, updatePassword} from "firebase/auth"; import {errorMessages} from "@/constants/errors"; import moment from "moment"; import ShortUniqueId from "short-unique-id"; import {Payment} from "@/interfaces/paypal"; -import { toFixedNumber } from "@/utils/number"; +import {toFixedNumber} from "@/utils/number"; const db = getFirestore(app); const auth = getAuth(app); @@ -22,10 +22,10 @@ export default withIronSessionApiRoute(handler, sessionOptions); // but if it is not inserted as a string, some UI components will not work (Invalid Date) const addPaymentRecord = async (data: any) => { await setDoc(doc(db, "payments", data.id), data); -} +}; const managePaymentRecords = async (user: User, userId: string | undefined): Promise => { try { - if(user.type === 'corporate' && userId) { + if (user.type === "corporate" && userId) { const shortUID = new ShortUniqueId(); const data: Payment = { id: shortUID.randomUUID(8), @@ -38,34 +38,35 @@ const managePaymentRecords = async (user: User, userId: string | undefined): Pro isPaid: false, date: new Date().toISOString(), }; - + const corporatePayments = await getDocs(query(collection(db, "payments"), where("corporate", "==", userId))); - if(corporatePayments.docs.length === 0) { + if (corporatePayments.docs.length === 0) { await addPaymentRecord(data); return true; } - + const hasPaymentPaidAndExpiring = corporatePayments.docs.filter((doc) => { const data = doc.data(); - return data.isPaid - && moment().isAfter(moment(user.subscriptionExpirationDate).subtract(30, "days")) - && moment().isBefore(moment(user.subscriptionExpirationDate)); + return ( + data.isPaid && + moment().isAfter(moment(user.subscriptionExpirationDate).subtract(30, "days")) && + moment().isBefore(moment(user.subscriptionExpirationDate)) + ); }); - if(hasPaymentPaidAndExpiring.length > 0) { + if (hasPaymentPaidAndExpiring.length > 0) { await addPaymentRecord(data); return true; } } return false; - } catch(e) { + } catch (e) { // if this process fails it should not stop the rest of the process console.log(e); return false; } - -} +}; async function handler(req: NextApiRequest, res: NextApiResponse) { if (!req.session.user) { @@ -108,6 +109,18 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { try { const credential = await signInWithEmailAndPassword(auth, req.session.user.email, updatedUser.password); await updateEmail(credential.user, updatedUser.email); + + const groups = ((await getDocs(collection(db, "groups"))).docs.map((x) => ({...x.data(), id: x.id})) as Group[]).filter((x) => + x.participants.includes(req.session.user!.id), + ); + + groups.forEach(async (group) => { + await setDoc( + doc(db, "groups", group.id), + {participants: group.participants.filter((x) => x !== req.session.user!.id)}, + {merge: true}, + ); + }); } catch { res.status(400).json({error: "E002", message: errorMessages.E002}); return; diff --git a/src/pages/profile.tsx b/src/pages/profile.tsx index 0c0ed4cc..f81a0fc3 100644 --- a/src/pages/profile.tsx +++ b/src/pages/profile.tsx @@ -19,6 +19,8 @@ import {shouldRedirectHome} from "@/utils/navigation.disabled"; import moment from "moment"; import {BsCamera, BsCameraFill} from "react-icons/bs"; import {USER_TYPE_LABELS} from "@/resources/user"; +import useGroups from "@/hooks/useGroups"; +import useUsers from "@/hooks/useUsers"; export const getServerSideProps = withIronSessionSsr(({req, res}) => { const user = req.session.user; @@ -52,28 +54,32 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => { interface Props { user: User; - mutateUser: Function, + mutateUser: Function; } -function UserProfile({ - user, - mutateUser, -}: Props) { - const [bio, setBio] = useState(user.bio || ''); - const [name, setName] = useState(user.name || ''); - const [email, setEmail] = useState(user.email || ''); +function UserProfile({user, mutateUser}: Props) { + const [bio, setBio] = useState(user.bio || ""); + const [name, setName] = useState(user.name || ""); + const [email, setEmail] = useState(user.email || ""); const [password, setPassword] = useState(""); const [newPassword, setNewPassword] = useState(""); const [isLoading, setIsLoading] = useState(false); const [profilePicture, setProfilePicture] = useState(user.profilePicture); - const [country, setCountry] = useState(user.demographicInformation?.country || ''); - const [phone, setPhone] = useState(user.demographicInformation?.phone || ''); + const [country, setCountry] = useState(user.demographicInformation?.country || ""); + const [phone, setPhone] = useState(user.demographicInformation?.phone || ""); const [gender, setGender] = useState(user.demographicInformation?.gender || undefined); - const [employment, setEmployment] = useState(user.type === "corporate" ? undefined : user.demographicInformation?.employment); + const [employment, setEmployment] = useState( + user.type === "corporate" ? undefined : user.demographicInformation?.employment, + ); const [position, setPosition] = useState(user.type === "corporate" ? user.demographicInformation?.position : undefined); - const [companyName, setCompanyName] = useState(user.type === 'agent' ? user.agentInformation?.companyName : undefined); - const [commercialRegistration, setCommercialRegistration] = useState(user.type === 'agent' ? user.agentInformation?.commercialRegistration : undefined); + const [companyName, setCompanyName] = useState(user.type === "agent" ? user.agentInformation?.companyName : undefined); + const [commercialRegistration, setCommercialRegistration] = useState( + user.type === "agent" ? user.agentInformation?.commercialRegistration : undefined, + ); + + const {groups} = useGroups(); + const {users} = useUsers(); const profilePictureInput = useRef(null); const expirationDateColor = (date: Date) => { @@ -120,6 +126,19 @@ function UserProfile({ return; } + if (email !== user?.email) { + const userAdmins = groups.filter((x) => x.participants.includes(user.id)).map((x) => x.admin); + const message = + users.filter((x) => userAdmins.includes(x.id) && x.type === "corporate").length > 0 + ? "If you change your e-mail address, you will lose all benefits from your university/institute. Are you sure you want to continue?" + : "Are you sure you want to update your e-mail address?"; + + if (!confirm(message)) { + setIsLoading(false); + return; + } + } + const request = await axios.post("/api/users/update", { bio, name, @@ -176,7 +195,7 @@ function UserProfile({
setPassword(e)} @@ -358,10 +377,10 @@ function UserProfile({
{USER_TYPE_LABELS[user.type]}
- {user.type === 'agent' && ( + {user.type === "agent" && (
{user.demographicInformation?.country.toLowerCase() @@ -394,9 +413,8 @@ function UserProfile({ ); } - export default function Home() { - const {user, mutateUser } = useUser({redirectTo: "/login"}); + const {user, mutateUser} = useUser({redirectTo: "/login"}); return ( <> @@ -410,9 +428,7 @@ export default function Home() { - {user && } + {user && } ); - - -} \ No newline at end of file +}