feat(auth): Implement LoginForm, RegisterForm and integrate with App

This commit is contained in:
Yunxiao Xu
2026-02-11 20:32:23 -08:00
parent f8612cfcb8
commit 42f982e373
8 changed files with 595 additions and 1 deletions

View File

@@ -0,0 +1,106 @@
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { loginSchema, type LoginInput } from "@/lib/validations/auth"
import { AuthService } from "@/services/auth"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { useState } from "react"
interface LoginFormProps {
onSuccess: () => void
onToggleMode: () => void
}
export function LoginForm({ onSuccess, onToggleMode }: LoginFormProps) {
const [error, setError] = useState<string | null>(null)
const [isLoading, setIsLoading] = useState(false)
const form = useForm<LoginInput>({
resolver: zodResolver(loginSchema),
defaultValues: {
email: "",
password: "",
},
})
async function onSubmit(values: LoginInput) {
setIsLoading(true)
setError(null)
try {
await AuthService.login(values.email, values.password)
onSuccess()
} catch (err: any) {
setError(err.response?.data?.detail || "Login failed. Please check your credentials.")
} finally {
setIsLoading(false)
}
}
return (
<Card className="w-full max-w-md mx-auto">
<CardHeader>
<CardTitle>Login</CardTitle>
<CardDescription>Enter your email and password to access your account.</CardDescription>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
{error && (
<div className="p-3 text-sm font-medium text-destructive bg-destructive/10 rounded-md">
{error}
</div>
)}
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="name@example.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input type="password" placeholder="••••••••" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full" disabled={isLoading}>
{isLoading ? "Logging in..." : "Login"}
</Button>
</form>
</Form>
<div className="mt-4 text-center text-sm">
Don't have an account?{" "}
<button
type="button"
className="underline underline-offset-4 hover:text-primary"
onClick={onToggleMode}
>
Register
</button>
</div>
</CardContent>
</Card>
)
}