diff --git a/prisma/postgres/schema.prisma b/prisma/postgres/schema.prisma index a18a115..cbedef3 100644 --- a/prisma/postgres/schema.prisma +++ b/prisma/postgres/schema.prisma @@ -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()) @@ -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 diff --git a/src/components/StyledCheckbox.tsx b/src/components/StyledCheckbox.tsx index 959a79f..5ce697e 100644 --- a/src/components/StyledCheckbox.tsx +++ b/src/components/StyledCheckbox.tsx @@ -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, HTMLInputElement>) => { - const setInd = useCallback((el: HTMLInputElement) => { - if (el) { - el.indeterminate = indeterminate ?? false; - } - }, [indeterminate]); - return
- - -
; +export const StyledCheckbox = ({ + indeterminate, + ...others +}: { indeterminate?: boolean } & DetailedHTMLProps< + InputHTMLAttributes, + HTMLInputElement +>) => { + const setInd = useCallback( + (el: HTMLInputElement) => { + if (el) { + el.indeterminate = indeterminate ?? false; + } + }, + [indeterminate], + ); + return ( +
+ + +
+ ); }; diff --git a/src/components/UpdatePasswordOverlayModal.tsx b/src/components/UpdatePasswordOverlayModal.tsx new file mode 100644 index 0000000..0513638 --- /dev/null +++ b/src/components/UpdatePasswordOverlayModal.tsx @@ -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; +type UpdatePasswordModalProps = { + onUpdate: (data: UpdatePasswordSchemaData) => void; + showModal: boolean; + closeModal: () => void; +}; +const UpdatePasswordModal = ({ + onUpdate, + showModal, + closeModal, +}: UpdatePasswordModalProps) => { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(updatePasswordSchema_z), + }); + + const onSubmit: SubmitHandler = (data) => { + onUpdate(data); + }; + + return ( + <> + {showModal && ( + <> +
+
+
+ +

Change Password

+
+
void handleSubmit(onSubmit)(e)} + > +
+ + + {errors.password && ( + + {errors.password.message} + + )} +
+
+ +
+
+
+
+
+
+
+ + )} + + ); +}; + +export default UpdatePasswordModal; diff --git a/src/pages/profile/account.tsx b/src/pages/profile/account.tsx index f78b56c..5c5186f 100644 --- a/src/pages/profile/account.tsx +++ b/src/pages/profile/account.tsx @@ -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 @@ -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, @@ -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; }, }); @@ -130,6 +130,13 @@ const ProfilePage: NextPage = () => { Profile + setIsEditingPassword(false)} + onUpdate={(data) => + updatePassword({ id: userData.id, password: data.password }) + } + />
{
Account information - {isEditing ? ( + {isEditingUser ? ( ) : (
- {isEditing && ( + {isEditingUser && ( <>
{ />
{