change how the auth page looks and reset the captcha during auth failures
All checks were successful
Deploy / deploy (ubuntu-latest, 2.44.0) (push) Successful in 1m29s

This commit is contained in:
Braydon 2024-09-17 17:15:45 -04:00
parent 673bfb6fe7
commit 0e77042553
3 changed files with 34 additions and 19 deletions

BIN
bun.lockb

Binary file not shown.

@ -38,13 +38,14 @@
"zustand": "^5.0.0-rc.2" "zustand": "^5.0.0-rc.2"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.2.8",
"postcss": "^8", "postcss": "^8",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"eslint": "^8", "turnstile-types": "^1.2.2",
"eslint-config-next": "14.2.8" "typescript": "^5"
} }
} }

@ -19,30 +19,29 @@ import { Cookies, useCookies } from "next-client-cookies";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime";
import { motion } from "framer-motion"; 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. * Define the form schemas for the various stages.
*/ */
const EmailSchema = z.object({ 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({ 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(), username: z.string(),
password: z.string(), password: z.string(),
passwordConfirmation: z.string(), passwordConfirmation: z.string(),
// captchaResponse: z.string(),
}); });
const LoginSchema = z.object({ const LoginSchema = z.object({
email: z.union([ email: z.union([
z.string().email("Must be a valid email address"), z.string().email("Must be a valid email address."),
z.string({ message: "Must be a valid username" }), z.string({ message: "Must be a valid username." }),
]), ]),
password: z.string(), password: z.string(),
// captchaResponse: z.string(),
}); });
/** /**
@ -65,6 +64,7 @@ const AuthForm = (): ReactElement => {
undefined undefined
); );
const [error, setError] = useState<string | undefined>(undefined); const [error, setError] = useState<string | undefined>(undefined);
const turnstile: TurnstileObject = useTurnstile();
const cookies: Cookies = useCookies(); const cookies: Cookies = useCookies();
const router: AppRouterInstance = useRouter(); const router: AppRouterInstance = useRouter();
@ -118,7 +118,12 @@ const AuthForm = (): ReactElement => {
}, },
}); });
setError(error?.message ?? undefined); 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), { cookies.set("session", JSON.stringify(data), {
expires: expires:
((data?.expires as number) - Date.now()) / 86_400_000, ((data?.expires as number) - Date.now()) / 86_400_000,
@ -212,21 +217,30 @@ const AuthForm = (): ReactElement => {
onVerify={(token: string) => setCaptchaResponse(token)} onVerify={(token: string) => setCaptchaResponse(token)}
/> />
{/* Errors */} {/* Display the global error if it exists, otherwise show the first field error */}
{error && <p className="text-red-500">{error}</p>} <p className="-mt-2 pb-0.5 text-red-500">
{error
? error
: Object.values(errors).find((err: any) => err?.message) &&
Object.values(errors)
.find((err: any) => err?.message)
?.message?.toString()}
</p>
{/* Submit Form */} {/* Submit Form */}
<Button <Button
className="h-11 flex gap-2 items-center bg-zinc-800/75 text-white border border-zinc-700/35 hover:bg-zinc-800/75 hover:opacity-75 transition-all transform-gpu group" className="h-11 flex gap-2.5 items-center bg-zinc-800/75 text-white border border-zinc-700/35 hover:bg-zinc-800/75 hover:opacity-75 transition-all transform-gpu group"
type="submit" type="submit"
disabled={loading} disabled={loading}
> >
{loading && <ArrowPathIcon className="w-4 h-4 animate-spin" />} {loading && <ArrowPathIcon className="w-4 h-4 animate-spin" />}
{stage === "email" <span className="-translate-y-0.5">
? "Continue" {stage === "email"
: stage === "register" ? "Continue"
? "Register" : stage === "register"
: "Login"} ? "Register"
: "Login"}
</span>
{!loading && <AnimatedRightChevron />} {!loading && <AnimatedRightChevron />}
</Button> </Button>
</form> </form>