From 13a840ff786b61a9d3151f1839b424fac7520a9a Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Wed, 7 Jun 2023 12:33:33 +0000 Subject: [PATCH] Password validation with zod --- apps/web/components/reset-password.tsx | 41 ++++++++++++++++++-------- package-lock.json | 31 ++++++++++++++++++- package.json | 6 ++-- 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/apps/web/components/reset-password.tsx b/apps/web/components/reset-password.tsx index 53ece210c..03dc6df44 100644 --- a/apps/web/components/reset-password.tsx +++ b/apps/web/components/reset-password.tsx @@ -4,21 +4,35 @@ import { useRouter } from "next/router"; import { Button } from "@documenso/ui"; import Logo from "./logo"; import { ArrowLeftIcon } from "@heroicons/react/24/outline"; +import { zodResolver } from "@hookform/resolvers/zod"; import { FormProvider, useForm } from "react-hook-form"; import { toast } from "react-hot-toast"; +import * as z from "zod"; -interface ResetPasswordForm { - password: string; - confirmPassword: string; -} +const schema = z + .object({ + password: z.string().min(8, { message: "Password must be at least 8 characters" }), + confirmPassword: z.string().min(8, { message: "Password must be at least 8 characters" }), + }) + .refine((data) => data.password === data.confirmPassword, { + path: ["confirmPassword"], + message: "Password don't match", + }); + +type ResetPasswordForm = z.infer; export default function ResetPassword() { const router = useRouter(); const { token } = router.query; - const methods = useForm(); - const { register, formState, watch } = methods; - const password = watch("password", ""); + const methods = useForm({ + resolver: zodResolver(schema), + }); + const { + register, + formState: { errors, isSubmitting }, + handleSubmit, + } = methods; const [resetSuccessful, setResetSuccessful] = useState(false); @@ -67,7 +81,7 @@ export default function ResetPassword() { {resetSuccessful ? null : ( -
+
value === password || "The passwords do not match", - })} + {...register("confirmPassword")} id="confirmPassword" name="confirmPassword" type="password" @@ -103,10 +114,14 @@ export default function ResetPassword() {
+ {errors && ( + {errors.confirmPassword?.message} + )} +
diff --git a/package-lock.json b/package-lock.json index 8d104b0d7..20c623d5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@documenso/prisma": "*", "@headlessui/react": "^1.7.4", "@heroicons/react": "^2.0.13", + "@hookform/resolvers": "^3.1.0", "avatar-from-initials": "^1.0.3", "bcryptjs": "^2.4.3", "next": "13.2.4", @@ -24,7 +25,8 @@ "react-dom": "18.2.0", "react-hook-form": "^7.41.5", "react-hot-toast": "^2.4.0", - "react-signature-canvas": "^1.0.6" + "react-signature-canvas": "^1.0.6", + "zod": "^3.21.4" }, "devDependencies": { "@tailwindcss/forms": "^0.5.3", @@ -525,6 +527,14 @@ "react": ">= 16" } }, + "node_modules/@hookform/resolvers": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.1.0.tgz", + "integrity": "sha512-z0A8K+Nxq+f83Whm/ajlwE6VtQlp/yPHZnXw7XWVPIGm1Vx0QV8KThU3BpbBRfAZ7/dYqCKKBNnQh85BkmBKkA==", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -8048,6 +8058,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "packages/features": { "name": "@documenso/features", "version": "0.0.0" @@ -8507,6 +8525,12 @@ "integrity": "sha512-x89rFxH3SRdYaA+JCXwfe+RkE1SFTo9GcOkZettHer71Y3T7V+ogKmfw5CjTazgS3d0ClJ7p1NA+SP7VQLQcLw==", "requires": {} }, + "@hookform/resolvers": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.1.0.tgz", + "integrity": "sha512-z0A8K+Nxq+f83Whm/ajlwE6VtQlp/yPHZnXw7XWVPIGm1Vx0QV8KThU3BpbBRfAZ7/dYqCKKBNnQh85BkmBKkA==", + "requires": {} + }, "@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -14097,6 +14121,11 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zod": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==" } } } diff --git a/package.json b/package.json index 08d2a2788..9d333fa50 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@documenso/prisma": "*", "@headlessui/react": "^1.7.4", "@heroicons/react": "^2.0.13", + "@hookform/resolvers": "^3.1.0", "avatar-from-initials": "^1.0.3", "bcryptjs": "^2.4.3", "next": "13.2.4", @@ -35,7 +36,8 @@ "react-dom": "18.2.0", "react-hook-form": "^7.41.5", "react-hot-toast": "^2.4.0", - "react-signature-canvas": "^1.0.6" + "react-signature-canvas": "^1.0.6", + "zod": "^3.21.4" }, "devDependencies": { "@tailwindcss/forms": "^0.5.3", @@ -54,4 +56,4 @@ "turbo": "^1.9.9", "typescript": "4.8.4" } -} \ No newline at end of file +}