Skip to content

Commit

Permalink
Merge pull request #77 from CS3219-AY2324S1/update_password_modal
Browse files Browse the repository at this point in the history
Update password modal (also make profile errors generic)
  • Loading branch information
samyipsh authored Oct 30, 2023
2 parents d0c91d6 + c694d44 commit 0e18bab
Show file tree
Hide file tree
Showing 5 changed files with 376 additions and 201 deletions.
3 changes: 1 addition & 2 deletions prisma/postgres/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ model MatchRequest {
difficulty Difficulty
category String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// username String
userId String @unique
matchType MatchType @default(MANUAL)
createdAt DateTime @default(now())
Expand All @@ -154,7 +153,7 @@ model MatchRequest {
model JoinRequest {
id Int @id @default(autoincrement())
fromUser User @relation(fields: [fromId], references: [id], onDelete: Cascade)
fromId String // a user id todo(gab): rename this to something btr
fromId String // a user id todo(gab): rename this to something btr
// fromName String
toRequest MatchRequest @relation(fields: [toId], references: [id], onDelete: Cascade)
toId String
Expand Down
37 changes: 26 additions & 11 deletions src/components/StyledCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
import { useCallback, type DetailedHTMLProps, type InputHTMLAttributes } from "react";
import {
useCallback,
type DetailedHTMLProps,
type InputHTMLAttributes,
} from "react";

export const StyledCheckbox = ({ indeterminate, ...others }: { indeterminate?: boolean; } & DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>) => {
const setInd = useCallback((el: HTMLInputElement) => {
if (el) {
el.indeterminate = indeterminate ?? false;
}
}, [indeterminate]);
return <div className="checkbox tb-border">
<input type="checkbox" ref={setInd} {...others} />
<span className="checkmark" />
</div>;
export const StyledCheckbox = ({
indeterminate,
...others
}: { indeterminate?: boolean } & DetailedHTMLProps<
InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement
>) => {
const setInd = useCallback(
(el: HTMLInputElement) => {
if (el) {
el.indeterminate = indeterminate ?? false;
}
},
[indeterminate],
);
return (
<div className="checkbox tb-border">
<input type="checkbox" ref={setInd} {...others} />
<span className="checkmark" />
</div>
);
};
89 changes: 89 additions & 0 deletions src/components/UpdatePasswordOverlayModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from "react";
import { z } from "zod";
import { SubmitHandler, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const password_z = z.string().min(6);
const updatePasswordSchema_z = z.object({
password: password_z,
});

type UpdatePasswordSchemaData = z.infer<typeof updatePasswordSchema_z>;
type UpdatePasswordModalProps = {
onUpdate: (data: UpdatePasswordSchemaData) => void;
showModal: boolean;
closeModal: () => void;
};
const UpdatePasswordModal = ({
onUpdate,
showModal,
closeModal,
}: UpdatePasswordModalProps) => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<UpdatePasswordSchemaData>({
resolver: zodResolver(updatePasswordSchema_z),
});

const onSubmit: SubmitHandler<UpdatePasswordSchemaData> = (data) => {
onUpdate(data);
};

return (
<>
{showModal && (
<>
<div className="justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none rounded-md">
<div className="relative lg:w-1/3">
<div className="p-6 border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-slate-900 text-slate-100 outline-none focus:outline-none space-y-6">
<button
className="text-slate-300 absolute top-1 right-1 rounded-full font-bold p-2 text-sm outline-none focus:outline-none"
type="button"
onClick={() => closeModal()}
>
x
</button>
<h3>Change Password</h3>
<div className="relative flex-auto">
<form
className="flex flex-col items-stretch space-y-4 md:space-y-6"
onSubmit={(e) => void handleSubmit(onSubmit)(e)}
>
<div>
<label className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
Password
</label>
<input
className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
type="password"
placeholder="••••••••"
{...register("password")}
/>
{errors.password && (
<span className="text-sm lowercase text-red-300">
{errors.password.message}
</span>
)}
</div>
<div className="items-center py-1">
<input
className="w-full text-slate-200 bg-slate-700 hover:bg-slate-900 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-slate-500 dark:hover:bg-slate-600 dark:focus:ring-primary-800"
type="submit"
value="Update Password"
/>
</div>
</form>
</div>
</div>
</div>
</div>
<div className="opacity-70 fixed inset-0 z-40 bg-black"></div>
</>
)}
</>
);
};

export default UpdatePasswordModal;
76 changes: 45 additions & 31 deletions src/pages/profile/account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { signOut, useSession } from "next-auth/react";
import { LoadingPage } from "~/components/Loading";
import UpdatePasswordModal from "~/components/UpdatePasswordOverlayModal";

// TODO:
// - add email verification
Expand All @@ -23,9 +24,12 @@ const email_z = z.string().email().min(1);
const emailVerified_z = z.date().nullable();
const image_z = z.string().nullable();
const password_z = z.string().min(6);

const ProfilePage: NextPage = () => {
const router = useRouter();
const [isEditing, setIsEditing] = useState(false);
const [isEditingUser, setIsEditingUser] = useState(false);
const [isEditingPassword, setIsEditingPassword] = useState(false);

// session is `null` until nextauth fetches user's session data
const { data: session, update: updateSession } = useSession({
required: true,
Expand Down Expand Up @@ -61,36 +65,32 @@ const ProfilePage: NextPage = () => {
} = api.user.update.useMutation({
onSuccess: () => {
toast.success(`User updated`);
setIsEditing(false);
setIsEditingUser(false);

if (!newUserData) throw new Error("newUserData is undefined");
const { name, email, image } = newUserData;
const newUserDataForSession = { name, email, image };

setIsEditing(false);
setIsEditingUser(false);
void updateSession(newUserDataForSession);
},
onError: (e) => {
const errMsg = e.data?.zodError?.fieldErrors.content;
if (errMsg?.[0]) {
toast.error(`Failed to update: ${errMsg[0]}`);
}
toast.error(`Failed to update user: ${e.message}`);
toast.error(`Failed to update user`);
// const zodErrMsg = e.data?.zodError?.fieldErrors.content;
// reference zodErrMsg?.[0] for zod errors and e.message for generic errors
},
});

const { mutate: updatePassword, isLoading: isUpdatingPassword } =
api.user.updatePassword.useMutation({
onSuccess: () => {
setIsEditing(false);
setIsEditingUser(false);
setIsEditingPassword(false);
toast.success(`Password updated`);
},
onError: (e) => {
const errMsg = e.data?.zodError?.fieldErrors.content;
if (errMsg?.[0]) {
toast.error(`Failed to update password: ${errMsg[0]}`);
}
toast.error(`Failed to update password: ${e.message}`);
toast.error(`Failed to update password`);
// const zodErrMsg = e.data?.zodError?.fieldErrors.content;
},
});

Expand Down Expand Up @@ -130,6 +130,13 @@ const ProfilePage: NextPage = () => {
<title>Profile</title>
</Head>
<PageLayout>
<UpdatePasswordModal
showModal={isEditingPassword}
closeModal={() => setIsEditingPassword(false)}
onUpdate={(data) =>
updatePassword({ id: userData.id, password: data.password })
}
/>
<div className="relative h-48 bg-slate-600 border-b overscroll-y-scroll w-full border-x md:max-w-2xl">
<Image
src={imageURL ?? "https://picsum.photos/300/300"}
Expand Down Expand Up @@ -162,24 +169,24 @@ const ProfilePage: NextPage = () => {
<div className="p-4 w-1/2">
<div className="font-bold pb-2">
<span className="pr-3">Account information</span>
{isEditing ? (
{isEditingUser ? (
<button
className="font-medium text-primary-600 hover:underline dark:text-primary-500"
onClick={() => setIsEditing(false)}
onClick={() => setIsEditingUser(false)}
>
cancel
</button>
) : (
<button
onClick={() => setIsEditing(true)}
onClick={() => setIsEditingUser(true)}
className="font-medium text-primary-600 hover:underline dark:text-primary-500"
>
edit
</button>
)}
</div>
<div className="py-2" />
{isEditing && (
{isEditingUser && (
<>
<form
className="flex flex-col items-stretch space-y-4 md:space-y-6"
Expand Down Expand Up @@ -208,7 +215,7 @@ const ProfilePage: NextPage = () => {
/>
</div>
<input
className="w-full bg-opacity-60 dark:bg-opacity-60 text-white bg-primary-600 hover:bg-opacity-70 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800"
className="w-full uppercase text-slate-200 bg-slate-700 hover:bg-slate-900 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-slate-500 dark:hover:bg-slate-600 dark:focus:ring-primary-800"
type="submit"
value="Save Changes"
disabled={isSavingUserData}
Expand All @@ -217,18 +224,21 @@ const ProfilePage: NextPage = () => {
</form>
<div>
<button
onClick={() => {
const pw = prompt("enter new password");
const pwVerified = password_z.safeParse(pw);
if (pwVerified.success) {
void updatePassword({
id: userData.id,
password: pwVerified.data,
});
} else {
toast.error("invalid password");
}
}}
onClick={
() => setIsEditingPassword(true)
// const pw = prompt("enter new password");
// if (pw == null) return;

// const pwVerified = password_z.safeParse(pw);
// if (pwVerified.success) {
// void updatePassword({
// id: userData.id,
// password: pwVerified.data,
// });
// } else {
// toast.error("invalid password");
// }
}
className="text-neutral-400 rounded-md underline pr-2"
>
change password
Expand Down Expand Up @@ -269,4 +279,8 @@ const ProfilePage: NextPage = () => {
);
};

// const PasswordChangeModal = ({ isOpen }: { isOpen: boolean }) => {
// return <OverlayModal isOpen={isOpen}>hi</OverlayModal>;
// };

export default ProfilePage;
Loading

0 comments on commit 0e18bab

Please sign in to comment.