Files
memegoat/frontend/src/app/(auth)/login/page.tsx
Mathis HERRIOT 9ccbd2ceb1 refactor: improve formatting, type safety, and component organization
- Adjusted inconsistent formatting for better readability across components and services.
- Enhanced type safety by adding placeholders for ignored error parameters and improving types across services.
- Improved component organization by reordering imports consistently and applying formatting updates in UI components.
2026-01-29 14:11:28 +01:00

200 lines
5.2 KiB
TypeScript

"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { ArrowLeft } from "lucide-react";
import Link from "next/link";
import * as React from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp";
import { useAuth } from "@/providers/auth-provider";
const loginSchema = z.object({
email: z.string().email({ message: "Email invalide" }),
password: z
.string()
.min(8, { message: "Le mot de passe doit faire au moins 8 caractères" }),
});
type LoginFormValues = z.infer<typeof loginSchema>;
export default function LoginPage() {
const { login, verify2fa } = useAuth();
const [loading, setLoading] = React.useState(false);
const [show2fa, setShow2fa] = React.useState(false);
const [userId, setUserId] = React.useState<string | null>(null);
const [otpValue, setOtpValue] = React.useState("");
const form = useForm<LoginFormValues>({
resolver: zodResolver(loginSchema),
defaultValues: {
email: "",
password: "",
},
});
async function onSubmit(values: LoginFormValues) {
setLoading(true);
try {
const res = await login(values.email, values.password);
if (res.userId && res.message === "Please provide 2FA token") {
setUserId(res.userId);
setShow2fa(true);
}
} catch (_error) {
// Error is handled in useAuth via toast
} finally {
setLoading(false);
}
}
async function onOtpSubmit(e: React.FormEvent) {
e.preventDefault();
if (!userId || otpValue.length !== 6) return;
setLoading(true);
try {
await verify2fa(userId, otpValue);
} catch (_error) {
// Error handled in useAuth
} finally {
setLoading(false);
}
}
return (
<div className="min-h-screen flex items-center justify-center bg-zinc-50 dark:bg-zinc-950 p-4">
<div className="w-full max-w-md space-y-4">
<Link
href="/"
className="inline-flex items-center text-sm text-muted-foreground hover:text-primary transition-colors"
>
<ArrowLeft className="mr-2 h-4 w-4" />
Retour à l'accueil
</Link>
<Card>
<CardHeader>
<CardTitle className="text-2xl">
{show2fa ? "Double Authentification" : "Connexion"}
</CardTitle>
<CardDescription>
{show2fa
? "Entrez le code à 6 chiffres de votre application d'authentification."
: "Entrez vos identifiants pour accéder à votre compte MemeGoat."}
</CardDescription>
</CardHeader>
<CardContent>
{show2fa ? (
<form
onSubmit={onOtpSubmit}
className="space-y-6 flex flex-col items-center"
>
<InputOTP
maxLength={6}
value={otpValue}
onChange={(value) => setOtpValue(value)}
>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
<InputOTPSeparator />
<InputOTPGroup>
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
<Button
type="submit"
className="w-full"
disabled={loading || otpValue.length !== 6}
>
{loading ? "Vérification..." : "Vérifier le code"}
</Button>
<Button
variant="ghost"
className="w-full"
onClick={() => setShow2fa(false)}
disabled={loading}
>
Retour
</Button>
</form>
) : (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="goat@example.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Mot de passe</FormLabel>
<FormControl>
<Input type="password" placeholder="" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full" disabled={loading}>
{loading ? "Connexion en cours..." : "Se connecter"}
</Button>
</form>
</Form>
)}
</CardContent>
<CardFooter className="flex flex-col space-y-2">
<p className="text-sm text-center text-muted-foreground">
Vous n'avez pas de compte ?{" "}
<Link
href="/register"
className="text-primary hover:underline font-medium"
>
S'inscrire
</Link>
</p>
</CardFooter>
</Card>
</div>
</div>
);
}