diff --git a/bun.lockb b/bun.lockb index 5cc6e5e..35c0017 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 5de44ae..1667f7a 100644 --- a/package.json +++ b/package.json @@ -38,13 +38,14 @@ "zustand": "^5.0.0-rc.2" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.2.8", "postcss": "^8", "tailwindcss": "^3.4.1", - "eslint": "^8", - "eslint-config-next": "14.2.8" + "turnstile-types": "^1.2.2", + "typescript": "^5" } } diff --git a/src/components/auth/auth-form.tsx b/src/components/auth/auth-form.tsx index 59d3580..1d458fc 100644 --- a/src/components/auth/auth-form.tsx +++ b/src/components/auth/auth-form.tsx @@ -19,30 +19,29 @@ import { Cookies, useCookies } from "next-client-cookies"; import { useRouter } from "next/navigation"; import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; import { motion } from "framer-motion"; -import Turnstile from "react-turnstile"; +import Turnstile, { useTurnstile } from "react-turnstile"; +import { TurnstileObject } from "turnstile-types"; /** * Define the form schemas for the various stages. */ const EmailSchema = z.object({ - email: z.string().email("Must be a valid email address"), + email: z.string().email("Must be a valid email address."), }); const RegisterSchema = z.object({ - email: z.string().email("Must be a valid email address"), + email: z.string().email("Must be a valid email address."), username: z.string(), password: z.string(), passwordConfirmation: z.string(), - // captchaResponse: z.string(), }); const LoginSchema = z.object({ email: z.union([ - z.string().email("Must be a valid email address"), - z.string({ message: "Must be a valid username" }), + z.string().email("Must be a valid email address."), + z.string({ message: "Must be a valid username." }), ]), password: z.string(), - // captchaResponse: z.string(), }); /** @@ -65,6 +64,7 @@ const AuthForm = (): ReactElement => { undefined ); const [error, setError] = useState(undefined); + const turnstile: TurnstileObject = useTurnstile(); const cookies: Cookies = useCookies(); const router: AppRouterInstance = useRouter(); @@ -118,7 +118,12 @@ const AuthForm = (): ReactElement => { }, }); setError(error?.message ?? undefined); - if (!error) { + + // Reset the captcha if auth fails + if (error) { + turnstile.reset(); + } else { + // Otherwise store the session and redirect to the dashboard cookies.set("session", JSON.stringify(data), { expires: ((data?.expires as number) - Date.now()) / 86_400_000, @@ -212,21 +217,30 @@ const AuthForm = (): ReactElement => { onVerify={(token: string) => setCaptchaResponse(token)} /> - {/* Errors */} - {error &&

{error}

} + {/* Display the global error if it exists, otherwise show the first field error */} +

+ {error + ? error + : Object.values(errors).find((err: any) => err?.message) && + Object.values(errors) + .find((err: any) => err?.message) + ?.message?.toString()} +

{/* Submit Form */}