Skip to content

Commit

Permalink
feat: update user password UI
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoEscaleira committed Mar 20, 2024
1 parent 658b40f commit 4eb1b1a
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/__generated__/gql.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions src/__generated__/graphql.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

140 changes: 140 additions & 0 deletions src/pages/Profile/UpdatePasswordModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { useState } from "react";
import { useMutation } from "@apollo/client";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button, Dialog, DialogBody, DialogHeader, Typography } from "@material-tailwind/react";
import { Info } from "lucide-react";
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
import { toast } from "react-toastify";
import { z } from "zod";
import { FormInput } from "@components/Form";
import { useUserStore } from "@state/userStore";
import { UPDATE_USER_PASSWORD } from "@utils/queries/UpdateUserPassword";

const formSchema = z
.object({
currentPassword: z.string().min(1, { message: "Enter your current password." }),
password: z.string().min(1, { message: "Enter a new password." }),
passwordConfirm: z.string().min(1, { message: "Enter a new confirm password." }),
})
.superRefine(({ password, passwordConfirm }, ctx) => {
if (password !== passwordConfirm) {
ctx.addIssue({
code: "custom",
message: "The new passwords did not match",
path: ["passwordConfirm"],
});
}
});

export const UpdatePasswordModal = () => {
const [open, setOpen] = useState(false);
const toggle = () => setOpen(!open);

const {
user: { userId },
} = useUserStore();

const [updatePassword, { loading, error: mutationError }] = useMutation(UPDATE_USER_PASSWORD);

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
currentPassword: "",
password: "",
passwordConfirm: "",
},
});
const {
handleSubmit,
reset,
formState: { errors },
} = form;

const onSubmit: SubmitHandler<z.infer<typeof formSchema>> = async (values, event) => {
event?.preventDefault();
try {
await updatePassword({
variables: {
userPasswordInput: {
userId,
currentPassword: values.currentPassword,
password: values.password,
passwordConfirm: values.passwordConfirm,
},
},
});
reset();
toggle();
toast.success("Password updated successfully!");
} catch (e) {
console.log("Something went wrong", e);
}
};

return (
<>
<Button onClick={toggle} variant="outlined">
Update password
</Button>
<Dialog open={open} handler={toggle} size="sm" className="outline-none">
<DialogHeader>
<Typography className="text-xl font-medium">Update your password</Typography>
</DialogHeader>
<DialogBody>
<FormProvider {...form}>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6 py-4">
<FormInput
name="currentPassword"
type="password"
label="Current Password"
placeholder="*******"
fieldError={errors.currentPassword}
/>
<FormInput
name="password"
type="password"
label="Password"
placeholder="*******"
fieldError={errors.password}
/>
<FormInput
name="passwordConfirm"
type="password"
label="Password confirmation"
placeholder="*******"
fieldError={errors.passwordConfirm}
/>

<Typography variant="small" color="gray" className="mt-2 flex items-center gap-1 font-normal">
<Info className="w-7 md:w-5" />
Use at least 8 characters, one uppercase, one lowercase and one number and one special character.
</Typography>

{mutationError?.message && (
<Typography variant="small" color="red">
{mutationError.message}
</Typography>
)}

<div className="flex w-full justify-end gap-3">
<Button
variant="outlined"
color="gray"
onClick={() => {
toggle();
reset();
}}
>
Cancel
</Button>
<Button variant="gradient" color="blue" loading={loading} type="submit">
Update
</Button>
</div>
</form>
</FormProvider>
</DialogBody>
</Dialog>
</>
);
};
11 changes: 8 additions & 3 deletions src/pages/Profile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useUserStore } from "@state/userStore.ts";
import { DATE_TIME, DATE_TIME_READ } from "@utils/constants.ts";
import { UPDATE_USER } from "@utils/queries/UpdateUser";
import { DownloadAttempts } from "./DownloadAttempts";
import { UpdatePasswordModal } from "./UpdatePasswordModal";

const formSchema = z.object({
firstName: z.string().min(1, { message: "Enter a first name." }),
Expand Down Expand Up @@ -138,9 +139,13 @@ export function Component() {
</form>
</FormProvider>
{!isEditing && (
<Button type="button" fullWidth variant="outlined" onClick={() => setIsEditing(true)} className="my-4 w-80">
Edit profile
</Button>
<div className="my-4 flex w-80 flex-col gap-4">
<Button type="button" variant="outlined" onClick={() => setIsEditing(true)}>
Edit profile
</Button>

<UpdatePasswordModal />
</div>
)}
<UserRoleChip role={role} />
<Typography className="pt-6 text-center" variant="small">
Expand Down
7 changes: 7 additions & 0 deletions src/utils/queries/UpdateUserPassword.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { gql } from "@generated/gql.ts";

export const UPDATE_USER_PASSWORD = gql(/* GraphQL */ `
mutation UpdateUserPassword($userPasswordInput: PasswordUpdateInput!) {
updatePassword(userPasswordInput: $userPasswordInput)
}
`);

0 comments on commit 4eb1b1a

Please sign in to comment.