Made it so that students, connected to a corporate, if they change their e-mail, they get unassigned

This commit is contained in:
Tiago Ribeiro
2024-01-02 11:07:18 +00:00
parent 1c2c3fe402
commit d2276eba1d
4 changed files with 70 additions and 40 deletions

View File

@@ -22,8 +22,8 @@ interface Props {
export default function Diagnostic({onFinish}: Props) { export default function Diagnostic({onFinish}: Props) {
const [focus, setFocus] = useState<"academic" | "general">(); const [focus, setFocus] = useState<"academic" | "general">();
const [levels, setLevels] = useState({reading: -1, listening: -1, writing: -1, speaking: -1}); 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}); const [desiredLevels, setDesiredLevels] = useState({reading: 9, listening: 9, writing: 9, speaking: 9, level: 9});
const router = useRouter(); const router = useRouter();
@@ -51,7 +51,7 @@ export default function Diagnostic({onFinish}: Props) {
axios axios
.patch("/api/users/update", { .patch("/api/users/update", {
focus, 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, desiredLevels,
isFirstLogin: false, isFirstLogin: false,
}) })

View File

@@ -191,11 +191,12 @@ export default function StudentDashboard({user}: Props) {
{module === "listening" && <BsHeadphones className="text-ielts-listening w-4 h-4 md:w-5 md:h-5" />} {module === "listening" && <BsHeadphones className="text-ielts-listening w-4 h-4 md:w-5 md:h-5" />}
{module === "writing" && <BsPen className="text-ielts-writing w-4 h-4 md:w-5 md:h-5" />} {module === "writing" && <BsPen className="text-ielts-writing w-4 h-4 md:w-5 md:h-5" />}
{module === "speaking" && <BsMegaphone className="text-ielts-speaking w-4 h-4 md:w-5 md:h-5" />} {module === "speaking" && <BsMegaphone className="text-ielts-speaking w-4 h-4 md:w-5 md:h-5" />}
{module === "level" && <BsClipboard className="text-ielts-level w-4 h-4 md:w-5 md:h-5" />}
</div> </div>
<div className="flex justify-between w-full"> <div className="flex justify-between w-full">
<span className="font-bold md:font-extrabold text-sm">{capitalize(module)}</span> <span className="font-bold md:font-extrabold text-sm">{capitalize(module)}</span>
<span className="text-sm font-normal text-mti-gray-dim"> <span className="text-sm font-normal text-mti-gray-dim">
Level {user.levels[module]} / Level {user.desiredLevels[module]} Level {user.levels[module] || 0} / Level {user.desiredLevels[module] || 9}
</span> </span>
</div> </div>
</div> </div>

View File

@@ -4,7 +4,7 @@ import {app, storage} from "@/firebase";
import {getFirestore, collection, getDocs, getDoc, doc, setDoc, query, where} from "firebase/firestore"; import {getFirestore, collection, getDocs, getDoc, doc, setDoc, query, where} from "firebase/firestore";
import {withIronSessionApiRoute} from "iron-session/next"; import {withIronSessionApiRoute} from "iron-session/next";
import {sessionOptions} from "@/lib/session"; 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 {getDownloadURL, getStorage, ref, uploadBytes} from "firebase/storage";
import {getAuth, signInWithEmailAndPassword, updateEmail, updatePassword} from "firebase/auth"; import {getAuth, signInWithEmailAndPassword, updateEmail, updatePassword} from "firebase/auth";
import {errorMessages} from "@/constants/errors"; import {errorMessages} from "@/constants/errors";
@@ -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) // but if it is not inserted as a string, some UI components will not work (Invalid Date)
const addPaymentRecord = async (data: any) => { const addPaymentRecord = async (data: any) => {
await setDoc(doc(db, "payments", data.id), data); await setDoc(doc(db, "payments", data.id), data);
} };
const managePaymentRecords = async (user: User, userId: string | undefined): Promise<boolean> => { const managePaymentRecords = async (user: User, userId: string | undefined): Promise<boolean> => {
try { try {
if(user.type === 'corporate' && userId) { if (user.type === "corporate" && userId) {
const shortUID = new ShortUniqueId(); const shortUID = new ShortUniqueId();
const data: Payment = { const data: Payment = {
id: shortUID.randomUUID(8), id: shortUID.randomUUID(8),
@@ -47,9 +47,11 @@ const managePaymentRecords = async (user: User, userId: string | undefined): Pro
const hasPaymentPaidAndExpiring = corporatePayments.docs.filter((doc) => { const hasPaymentPaidAndExpiring = corporatePayments.docs.filter((doc) => {
const data = doc.data(); const data = doc.data();
return data.isPaid return (
&& moment().isAfter(moment(user.subscriptionExpirationDate).subtract(30, "days")) data.isPaid &&
&& moment().isBefore(moment(user.subscriptionExpirationDate)); moment().isAfter(moment(user.subscriptionExpirationDate).subtract(30, "days")) &&
moment().isBefore(moment(user.subscriptionExpirationDate))
);
}); });
if (hasPaymentPaidAndExpiring.length > 0) { if (hasPaymentPaidAndExpiring.length > 0) {
@@ -64,8 +66,7 @@ const managePaymentRecords = async (user: User, userId: string | undefined): Pro
console.log(e); console.log(e);
return false; return false;
} }
};
}
async function handler(req: NextApiRequest, res: NextApiResponse) { async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!req.session.user) { if (!req.session.user) {
@@ -108,6 +109,18 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
const credential = await signInWithEmailAndPassword(auth, req.session.user.email, updatedUser.password); const credential = await signInWithEmailAndPassword(auth, req.session.user.email, updatedUser.password);
await updateEmail(credential.user, updatedUser.email); 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 { } catch {
res.status(400).json({error: "E002", message: errorMessages.E002}); res.status(400).json({error: "E002", message: errorMessages.E002});
return; return;

View File

@@ -19,6 +19,8 @@ import {shouldRedirectHome} from "@/utils/navigation.disabled";
import moment from "moment"; import moment from "moment";
import {BsCamera, BsCameraFill} from "react-icons/bs"; import {BsCamera, BsCameraFill} from "react-icons/bs";
import {USER_TYPE_LABELS} from "@/resources/user"; import {USER_TYPE_LABELS} from "@/resources/user";
import useGroups from "@/hooks/useGroups";
import useUsers from "@/hooks/useUsers";
export const getServerSideProps = withIronSessionSsr(({req, res}) => { export const getServerSideProps = withIronSessionSsr(({req, res}) => {
const user = req.session.user; const user = req.session.user;
@@ -52,28 +54,32 @@ export const getServerSideProps = withIronSessionSsr(({req, res}) => {
interface Props { interface Props {
user: User; user: User;
mutateUser: Function, mutateUser: Function;
} }
function UserProfile({ function UserProfile({user, mutateUser}: Props) {
user, const [bio, setBio] = useState(user.bio || "");
mutateUser, const [name, setName] = useState(user.name || "");
}: Props) { const [email, setEmail] = useState(user.email || "");
const [bio, setBio] = useState(user.bio || '');
const [name, setName] = useState(user.name || '');
const [email, setEmail] = useState(user.email || '');
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [newPassword, setNewPassword] = useState(""); const [newPassword, setNewPassword] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [profilePicture, setProfilePicture] = useState(user.profilePicture); const [profilePicture, setProfilePicture] = useState(user.profilePicture);
const [country, setCountry] = useState<string>(user.demographicInformation?.country || ''); const [country, setCountry] = useState<string>(user.demographicInformation?.country || "");
const [phone, setPhone] = useState<string>(user.demographicInformation?.phone || ''); const [phone, setPhone] = useState<string>(user.demographicInformation?.phone || "");
const [gender, setGender] = useState<Gender | undefined>(user.demographicInformation?.gender || undefined); const [gender, setGender] = useState<Gender | undefined>(user.demographicInformation?.gender || undefined);
const [employment, setEmployment] = useState<EmploymentStatus | undefined>(user.type === "corporate" ? undefined : user.demographicInformation?.employment); const [employment, setEmployment] = useState<EmploymentStatus | undefined>(
user.type === "corporate" ? undefined : user.demographicInformation?.employment,
);
const [position, setPosition] = useState<string | undefined>(user.type === "corporate" ? user.demographicInformation?.position : undefined); const [position, setPosition] = useState<string | undefined>(user.type === "corporate" ? user.demographicInformation?.position : undefined);
const [companyName, setCompanyName] = useState<string | undefined>(user.type === 'agent' ? user.agentInformation?.companyName : undefined); const [companyName, setCompanyName] = useState<string | undefined>(user.type === "agent" ? user.agentInformation?.companyName : undefined);
const [commercialRegistration, setCommercialRegistration] = useState<string | undefined>(user.type === 'agent' ? user.agentInformation?.commercialRegistration : undefined); const [commercialRegistration, setCommercialRegistration] = useState<string | undefined>(
user.type === "agent" ? user.agentInformation?.commercialRegistration : undefined,
);
const {groups} = useGroups();
const {users} = useUsers();
const profilePictureInput = useRef(null); const profilePictureInput = useRef(null);
const expirationDateColor = (date: Date) => { const expirationDateColor = (date: Date) => {
@@ -120,6 +126,19 @@ function UserProfile({
return; 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", { const request = await axios.post("/api/users/update", {
bio, bio,
name, name,
@@ -176,7 +195,7 @@ function UserProfile({
</div> </div>
<div className="flex flex-col md:flex-row gap-8 w-full"> <div className="flex flex-col md:flex-row gap-8 w-full">
<Input <Input
label="Old Password" label="Current Password"
type="password" type="password"
name="password" name="password"
onChange={(e) => setPassword(e)} onChange={(e) => setPassword(e)}
@@ -358,10 +377,10 @@ function UserProfile({
</span> </span>
<h6 className="font-normal text-base text-mti-gray-taupe">{USER_TYPE_LABELS[user.type]}</h6> <h6 className="font-normal text-base text-mti-gray-taupe">{USER_TYPE_LABELS[user.type]}</h6>
</div> </div>
{user.type === 'agent' && ( {user.type === "agent" && (
<div className="flag items-center h-fit"> <div className="flag items-center h-fit">
<img <img
alt={user.demographicInformation?.country.toLowerCase() + '_flag'} alt={user.demographicInformation?.country.toLowerCase() + "_flag"}
src={`https://flagcdn.com/w320/${user.demographicInformation?.country.toLowerCase()}.png`} src={`https://flagcdn.com/w320/${user.demographicInformation?.country.toLowerCase()}.png`}
width="320" width="320"
/> />
@@ -394,7 +413,6 @@ function UserProfile({
); );
} }
export default function Home() { export default function Home() {
const {user, mutateUser} = useUser({redirectTo: "/login"}); const {user, mutateUser} = useUser({redirectTo: "/login"});
@@ -413,6 +431,4 @@ export default function Home() {
{user && <UserProfile user={user} mutateUser={mutateUser} />} {user && <UserProfile user={user} mutateUser={mutateUser} />}
</> </>
); );
} }