diff --git a/src/components/BlankQuestionsModal.tsx b/src/components/BlankQuestionsModal.tsx index cd1d9650..1a0b50aa 100644 --- a/src/components/BlankQuestionsModal.tsx +++ b/src/components/BlankQuestionsModal.tsx @@ -40,10 +40,10 @@ export default function BlankQuestionsModal({isOpen, onClose}: Props) { Are you sure you want to continue without completing those questions?
- -
diff --git a/src/components/Exercises/FillBlanks.tsx b/src/components/Exercises/FillBlanks.tsx index d8b5f00b..10d25baa 100644 --- a/src/components/Exercises/FillBlanks.tsx +++ b/src/components/Exercises/FillBlanks.tsx @@ -51,10 +51,10 @@ function WordsDrawer({words, isOpen, blankId, previouslySelectedWord, onCancel, ))}
- -
diff --git a/src/components/Exercises/MultipleChoice.tsx b/src/components/Exercises/MultipleChoice.tsx index 71dd2f68..9818c35c 100644 --- a/src/components/Exercises/MultipleChoice.tsx +++ b/src/components/Exercises/MultipleChoice.tsx @@ -94,11 +94,11 @@ export default function MultipleChoice({id, prompt, type, questions, userSolutio
- -
diff --git a/src/components/Low/Button.tsx b/src/components/Low/Button.tsx index 53c196dc..928a338a 100644 --- a/src/components/Low/Button.tsx +++ b/src/components/Low/Button.tsx @@ -3,26 +3,26 @@ import {ReactNode} from "react"; interface Props { children: ReactNode; - color?: "orange" | "green" | "blue"; + color?: "rose" | "purple" | "red"; variant?: "outline" | "solid"; className?: string; disabled?: boolean; onClick?: () => void; } -export default function Button({color = "green", variant = "solid", disabled = false, className, children, onClick}: Props) { +export default function Button({color = "purple", variant = "solid", disabled = false, className, children, onClick}: Props) { const colorClassNames: {[key in typeof color]: {[key in typeof variant]: string}} = { - green: { + purple: { solid: "bg-mti-purple-light text-white hover:bg-mti-purple disabled:text-mti-purple disabled:bg-mti-purple-ultralight selection:bg-mti-purple-dark", outline: "bg-transparent text-mti-purple-light border border-mti-purple-light hover:bg-mti-purple-light disabled:text-mti-purple disabled:bg-mti-purple-ultralight selection:bg-mti-purple-dark hover:text-white selection:text-white", }, - blue: { + red: { solid: "bg-mti-red-light text-white hover:bg-mti-red disabled:text-mti-red disabled:bg-mti-red-ultralight selection:bg-mti-red-dark", outline: "bg-transparent text-mti-red-light border border-mti-red-light hover:bg-mti-red-light disabled:text-mti-red disabled:bg-mti-red-ultralight selection:bg-mti-red-dark hover:text-white selection:text-white", }, - orange: { + rose: { solid: "bg-mti-orange-light text-white hover:bg-mti-orange disabled:text-mti-orange disabled:bg-mti-orange-ultralight selection:bg-mti-orange-dark", outline: "bg-transparent text-mti-orange-light border border-mti-orange-light hover:bg-mti-orange-light disabled:text-mti-orange disabled:bg-mti-orange-ultralight selection:bg-mti-orange-dark hover:text-white selection:text-white", diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index f68581ff..f71419d3 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -10,7 +10,7 @@ interface Props { export default function Navbar({user}: Props) { return (
-

eCrop

+

EnCoach

diff --git a/src/components/Solutions/FillBlanks.tsx b/src/components/Solutions/FillBlanks.tsx index 795c98f7..4750aef8 100644 --- a/src/components/Solutions/FillBlanks.tsx +++ b/src/components/Solutions/FillBlanks.tsx @@ -99,11 +99,11 @@ export default function FillBlanksSolutions({prompt, solutions, text, userSoluti
- -
diff --git a/src/components/Solutions/MatchSentences.tsx b/src/components/Solutions/MatchSentences.tsx index e3f67d6d..0e11f804 100644 --- a/src/components/Solutions/MatchSentences.tsx +++ b/src/components/Solutions/MatchSentences.tsx @@ -85,11 +85,11 @@ export default function MatchSentencesSolutions({options, prompt, sentences, use
- -
diff --git a/src/components/Solutions/MultipleChoice.tsx b/src/components/Solutions/MultipleChoice.tsx index 1ccf20c8..4c76d43b 100644 --- a/src/components/Solutions/MultipleChoice.tsx +++ b/src/components/Solutions/MultipleChoice.tsx @@ -102,11 +102,11 @@ export default function MultipleChoice({prompt, questions, userSolutions, onNext
- -
diff --git a/src/components/Solutions/Speaking.tsx b/src/components/Solutions/Speaking.tsx index 999e04bf..458e2a9a 100644 --- a/src/components/Solutions/Speaking.tsx +++ b/src/components/Solutions/Speaking.tsx @@ -71,11 +71,11 @@ export default function Speaking({title, text, prompts, userSolutions, onNext, o
- -
diff --git a/src/components/Solutions/WriteBlanks.tsx b/src/components/Solutions/WriteBlanks.tsx index 52de41db..7eff1bc7 100644 --- a/src/components/Solutions/WriteBlanks.tsx +++ b/src/components/Solutions/WriteBlanks.tsx @@ -127,11 +127,11 @@ export default function WriteBlanksSolutions({
- -
diff --git a/src/components/Solutions/Writing.tsx b/src/components/Solutions/Writing.tsx index dff0b78a..9728db92 100644 --- a/src/components/Solutions/Writing.tsx +++ b/src/components/Solutions/Writing.tsx @@ -96,11 +96,11 @@ export default function Writing({id, prompt, info, attachment, userSolutions, on
- -
diff --git a/src/constants/errors.ts b/src/constants/errors.ts new file mode 100644 index 00000000..82484137 --- /dev/null +++ b/src/constants/errors.ts @@ -0,0 +1,10 @@ +export type Error = "E001" | "E002"; +export interface ErrorMessage { + error: Error; + message: string; +} + +export const errorMessages: {[key in Error]: string} = { + E001: "Wrong password!", + E002: "Invalid e-mail", +}; diff --git a/src/exams/Finish.tsx b/src/exams/Finish.tsx index 9c5d9a75..37514577 100644 --- a/src/exams/Finish.tsx +++ b/src/exams/Finish.tsx @@ -191,7 +191,7 @@ export default function Finish({user, scores, modules, isLoading, onViewResults} - diff --git a/src/exams/Listening.tsx b/src/exams/Listening.tsx index 11bf5e76..48ad37ba 100644 --- a/src/exams/Listening.tsx +++ b/src/exams/Listening.tsx @@ -116,7 +116,7 @@ export default function Listening({exam, showSolutions = false, onFinish}: Props {exerciseIndex === -1 && ( - )} diff --git a/src/exams/Reading.tsx b/src/exams/Reading.tsx index 2f9dca57..28126a30 100644 --- a/src/exams/Reading.tsx +++ b/src/exams/Reading.tsx @@ -64,7 +64,7 @@ function TextModal({isOpen, title, content, onClose}: {isOpen: boolean; title: s
-
@@ -185,7 +185,7 @@ export default function Reading({exam, showSolutions = false, onFinish}: Props) )} {exerciseIndex === -1 && ( - )} diff --git a/src/pages/api/users/update.ts b/src/pages/api/users/update.ts index 038a9eb7..7fcd24a0 100644 --- a/src/pages/api/users/update.ts +++ b/src/pages/api/users/update.ts @@ -5,8 +5,13 @@ import {getFirestore, collection, getDocs, getDoc, doc, setDoc} from "firebase/f import {withIronSessionApiRoute} from "iron-session/next"; import {sessionOptions} from "@/lib/session"; import {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"; const db = getFirestore(app); +const storage = getStorage(app); +const auth = getAuth(app); export default withIronSessionApiRoute(handler, sessionOptions); @@ -17,7 +22,45 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { } const userRef = doc(db, "users", req.session.user.id); - await setDoc(userRef, req.body, {merge: true}); + const updatedUser = req.body as User & {password?: string; newPassword?: string}; + + if (updatedUser.profilePicture && updatedUser.profilePicture !== req.session.user.profilePicture) { + const profilePictureFiletype = updatedUser.profilePicture.split(";")[0].split("/")[1]; + const profilePictureRef = ref(storage, `profile_pictures/${req.session.user.id}.${profilePictureFiletype}`); + + const pictureBytes = Buffer.from(updatedUser.profilePicture, "base64url"); + const pictureSnapshot = await uploadBytes(profilePictureRef, pictureBytes); + + const pictureReference = ref(storage, pictureSnapshot.metadata.fullPath); + updatedUser.profilePicture = await getDownloadURL(pictureReference); + } + + if (updatedUser.newPassword && updatedUser.password) { + try { + const credential = await signInWithEmailAndPassword(auth, req.session.user.email, updatedUser.password); + await updatePassword(credential.user, updatedUser.newPassword); + } catch { + res.status(400).json({error: "E001", message: errorMessages.E001}); + return; + } + } + + if (updatedUser.email !== req.session.user.email && updatedUser.password) { + try { + const credential = await signInWithEmailAndPassword(auth, req.session.user.email, updatedUser.password); + await updateEmail(credential.user, updatedUser.email); + } catch { + res.status(400).json({error: "E002", message: errorMessages.E002}); + return; + } + } + + delete updatedUser.password; + delete updatedUser.newPassword; + + await setDoc(userRef, updatedUser, {merge: true}); + req.session.user = {...updatedUser, id: req.session.user.id}; + await req.session.save(); res.status(200).json({ok: true}); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index aaee2290..0d892085 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -133,13 +133,7 @@ export default function Home() {
Bio - - Patricia Smith is a dedicated and enthusiastic student. Her passion for knowledge drives her to constantly seek new - academic challenges. She is recognized for her exemplary work ethic, active participation in the classroom, and commitment - to helping her peers. Her insatiable curiosity has led her to explore a wide range of areas of study, making her a - versatile and adaptable learner. Patricia is a true academic leader, inspiring other students to pursue their own - educational goals. - + {user.bio || "Your bio will appear here..."}
Score History diff --git a/src/pages/login.tsx b/src/pages/login.tsx index 03486859..d33844e1 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -83,7 +83,7 @@ export default function Login() { Forgot Password? - -