Files
Reactive-Resume/src/routes/auth/verify-2fa.tsx
T
Amruth Pillai 0bc53b9c2a - simplify imports
- update translations
- convert image to base64 before sending to printer
- update development docs
2026-01-22 15:46:09 +01:00

124 lines
3.6 KiB
TypeScript

import { zodResolver } from "@hookform/resolvers/zod";
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
import { ArrowLeftIcon, CheckIcon } from "@phosphor-icons/react";
import { createFileRoute, Link, redirect, useNavigate, useRouter } from "@tanstack/react-router";
import { REGEXP_ONLY_DIGITS } from "input-otp";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import z from "zod";
import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem, FormMessage } from "@/components/ui/form";
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "@/components/ui/input-otp";
import { authClient } from "@/integrations/auth/client";
export const Route = createFileRoute("/auth/verify-2fa")({
component: RouteComponent,
beforeLoad: async ({ context }) => {
if (context.session) throw redirect({ to: "/dashboard", replace: true });
},
});
const formSchema = z.object({
code: z.string().length(6, "Code must be 6 digits"),
});
type FormValues = z.infer<typeof formSchema>;
function RouteComponent() {
const router = useRouter();
const navigate = useNavigate();
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
code: "",
},
});
const onSubmit = async (data: FormValues) => {
const toastId = toast.loading(t`Verifying code...`);
const { error } = await authClient.twoFactor.verifyTotp({
code: data.code,
});
if (error) {
toast.error(error.message, { id: toastId });
return;
}
router.invalidate();
toast.dismiss(toastId);
navigate({ to: "/dashboard", replace: true });
};
return (
<>
<div className="space-y-1 text-center">
<h1 className="font-bold text-2xl tracking-tight">
<Trans>Two-Factor Authentication</Trans>
</h1>
<div className="text-muted-foreground">
<Trans>Enter the verification code from your authenticator app</Trans>
</div>
</div>
<Form {...form}>
<form className="grid gap-6" onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="code"
render={({ field }) => (
<FormItem className="justify-self-center">
<FormControl>
<InputOTP
maxLength={6}
value={field.value}
pattern={REGEXP_ONLY_DIGITS}
onChange={field.onChange}
onComplete={form.handleSubmit(onSubmit)}
pasteTransformer={(pasted) => pasted.replaceAll("-", "")}
>
<InputOTPGroup>
<InputOTPSlot index={0} className="size-12" />
<InputOTPSlot index={1} className="size-12" />
<InputOTPSlot index={2} className="size-12" />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} className="size-12" />
<InputOTPSlot index={4} className="size-12" />
<InputOTPSlot index={5} className="size-12" />
</InputOTPGroup>
</InputOTP>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex gap-x-2">
<Button type="button" variant="outline" className="flex-1" asChild>
<Link to="/auth/login">
<ArrowLeftIcon />
<Trans>Back to Login</Trans>
</Link>
</Button>
<Button type="submit" className="flex-1">
<CheckIcon />
<Trans>Verify</Trans>
</Button>
</div>
</form>
<Button type="button" variant="link" className="h-auto justify-self-center p-0 text-sm" asChild>
<Link to="/auth/verify-2fa-backup">
<Trans>Lost access to your authenticator?</Trans>
</Link>
</Button>
</Form>
</>
);
}