Skip to content

Commit

Permalink
Merge pull request #95 from codersforcauses/issue-33-Create_edit_prof…
Browse files Browse the repository at this point in the history
…ile_modal

Issue 33 create edit profile modal
  • Loading branch information
K-Straiton authored Aug 3, 2024
2 parents bf8091c + 4bc7e87 commit 09d3376
Show file tree
Hide file tree
Showing 19 changed files with 1,436 additions and 1,294 deletions.
582 changes: 483 additions & 99 deletions client/package-lock.json

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
"prepare": "cd .. && husky client/.husky"
},
"dependencies": {
"@hookform/resolvers": "^3.9.0",
"@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@tanstack/react-query": "^5.45.1",
Expand All @@ -29,6 +29,7 @@
"axios": "^1.7.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"date-fns": "^3.6.0",
"is-inside-container": "^1.0.0",
"js-cookie": "^3.0.5",
Expand All @@ -40,8 +41,11 @@
"react": "^18.3.1",
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.52.1",
"react-icons": "^5.2.1",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.8"
},
"devDependencies": {
"@csstools/postcss-oklab-function": "^3.0.16",
Expand Down
187 changes: 187 additions & 0 deletions client/src/components/main/ChangePasswordModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
"use client";

import { zodResolver } from "@hookform/resolvers/zod";
import { ChevronRight, EyeIcon, EyeOffIcon } from "lucide-react";
import Image from "next/image";
import { createElement, useState } from "react";
import { Control, useForm } from "react-hook-form";
import { z } from "zod";

import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";

const imageLoader = () => {
return `https://ui-avatars.com/api/?name=${username}`;
};

const pwdFormSchema = z
.object({
oldPwd: z.string(),
newPwd: z.string(),
confirmPwd: z.string(),
})
.refine((data) => data.newPwd === data.confirmPwd, {
message: "Passwords do not match",
path: ["confirmPwd"],
});

const username = "John Doe";

type props = {
control: Control<any>;
name: string;
label: string;
placeholder: string;
};

function CustomPasswordFormField({ control, name, label, placeholder }: props) {
const [passwordVisibility, setPasswordVisibility] = useState(false);
return (
<FormField
control={control}
name={name}
render={({ field }) => (
<FormItem className="mx-auto flex w-full flex-col last:pb-4 md:max-5xl:w-1/2">
<FormLabel className="w-full text-base font-bold md:max-5xl:text-lg">
{label}
</FormLabel>
<FormControl>
<div className="relative">
<Input
type={passwordVisibility ? "text" : "password"}
className="w-full rounded-xl bg-secondary md:max-5xl:w-full md:max-5xl:rounded-lg md:max-5xl:text-lg"
placeholder={placeholder}
{...field}
/>
<span
className="absolute inset-y-0 right-0 flex cursor-pointer items-center p-3 text-muted-foreground"
onClick={() => setPasswordVisibility(!passwordVisibility)}
>
{passwordVisibility ? (
<EyeOffIcon className="h-6 w-6" strokeWidth={1.5} />
) : (
<EyeIcon className="h-6 w-6" strokeWidth={1.5} />
)}
</span>
</div>
</FormControl>
<FormMessage className="w-full px-1 pt-1" />
</FormItem>
)}
/>
);
}

const fields = [
{
label: "Current password",
name: "oldPwd",
placeholder: "",
},
{
label: "New password",
name: "newPwd",
placeholder: "",
},
{
label: "Confirm new password",
name: "confirmPwd",
placeholder: "",
},
];

export default function ChangePasswordModal() {
const pwdForm = useForm<z.infer<typeof pwdFormSchema>>({
resolver: zodResolver(pwdFormSchema),
defaultValues: {
oldPwd: "",
newPwd: "",
confirmPwd: "",
},
});

return (
<Dialog>
<DialogTrigger asChild>
<Button
role="link"
variant="link"
className="inline-flex h-8 items-center px-2 text-base font-semibold text-primary md:max-5xl:text-lg"
>
Change Password
<ChevronRight size={20} />
</Button>
</DialogTrigger>
<DialogContent className="md:max-5xl:5/6 w-11/12 max-w-[900px] rounded-lg p-4 md:max-5xl:px-10 md:max-5xl:py-8">
<DialogHeader>
<DialogTitle className="my-2 ml-2 text-left text-2xl md:max-5xl:text-3xl md:max-5xl:font-bold">
Change Password
</DialogTitle>
<div className="border-t border-primary"></div>
</DialogHeader>
<div className="mt-2 grid w-full place-items-center">
<Image
loader={imageLoader}
src="jd.png"
alt="Profile Picture"
width={128}
height={128}
className="rounded-full md:max-5xl:w-32"
/>
<h2 className="pt-3 text-lg font-semibold text-black">{username}</h2>
</div>

{/* FORM SECTION */}

{/* Change Password Form */}
<Form {...pwdForm}>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
return pwdForm.handleSubmit((data) => console.log(data))(e);
}}
className="mt-6 space-y-6"
>
<div className="space-y-6">
{fields.map((field) => (
<CustomPasswordFormField
key={field.name}
control={pwdForm.control}
name={field.name}
label={field.label}
placeholder={field.placeholder}
/>
))}
</div>

<div className="flex flex-row justify-center">
<Button
className="my-2 h-10 px-2 max-md:w-full max-sm:mt-2 md:max-5xl:w-6/12 md:max-5xl:text-lg"
variant="outline"
type="submit"
>
Update Profile
</Button>
</div>
</form>
</Form>
</DialogContent>
</Dialog>
);
}
Loading

0 comments on commit 09d3376

Please sign in to comment.