From 5e1e9793771c1f937b86900cbc789c7a69f35415 Mon Sep 17 00:00:00 2001 From: dmolinari Date: Tue, 14 Apr 2026 11:21:53 -0300 Subject: [PATCH] refactor(web): LoginPage con shadcn Form, zod validation y Alert destructive --- .../features/auth/components/LoginForm.tsx | 132 +++++++++++------- src/web/src/features/auth/pages/LoginPage.tsx | 58 +++++--- 2 files changed, 115 insertions(+), 75 deletions(-) diff --git a/src/web/src/features/auth/components/LoginForm.tsx b/src/web/src/features/auth/components/LoginForm.tsx index 860b61c..69276cf 100644 --- a/src/web/src/features/auth/components/LoginForm.tsx +++ b/src/web/src/features/auth/components/LoginForm.tsx @@ -1,66 +1,90 @@ -import type { FormEvent } from 'react' +import { useForm } from 'react-hook-form' +import { zodResolver } from '@hookform/resolvers/zod' +import { z } from 'zod' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form' + +const loginSchema = z.object({ + username: z.string().min(1, 'El usuario es requerido'), + password: z.string().min(1, 'La contraseña es requerida'), +}) + +type LoginFormValues = z.infer interface LoginFormProps { onSubmit: (username: string, password: string) => void isLoading: boolean - error: string | null } -export function LoginForm({ onSubmit, isLoading, error }: LoginFormProps) { - function handleSubmit(e: FormEvent) { - e.preventDefault() - const form = e.currentTarget - const data = new FormData(form) - const username = data.get('username') as string - const password = data.get('password') as string - onSubmit(username, password) +export function LoginForm({ onSubmit, isLoading }: LoginFormProps) { + const form = useForm({ + resolver: zodResolver(loginSchema), + defaultValues: { username: '', password: '' }, + }) + + function handleSubmit(values: LoginFormValues) { + onSubmit(values.username, values.password) } return ( -
-
- - -
- -
- - -
- - {error && ( -

- {error} -

- )} - - -
+ ( + + Usuario + + + + + + )} + /> + + ( + + Contraseña + + + + + + )} + /> + + + + ) } diff --git a/src/web/src/features/auth/pages/LoginPage.tsx b/src/web/src/features/auth/pages/LoginPage.tsx index 08aa325..44b5ad6 100644 --- a/src/web/src/features/auth/pages/LoginPage.tsx +++ b/src/web/src/features/auth/pages/LoginPage.tsx @@ -1,7 +1,25 @@ import { useNavigate } from 'react-router-dom' +import { isAxiosError } from 'axios' +import { AlertCircle } from 'lucide-react' import { useLogin } from '../hooks/useLogin' import { LoginForm } from '../components/LoginForm' -import { isAxiosError } from 'axios' +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card' +import { Alert, AlertDescription } from '@/components/ui/alert' + +function resolveErrorMessage(err: unknown): string | null { + if (!err) return null + if (isAxiosError(err) && err.response?.data) { + const data = err.response.data as { error?: string } + return data.error ?? 'Error al iniciar sesión' + } + return 'Error al iniciar sesión' +} export function LoginPage() { const navigate = useNavigate() @@ -18,27 +36,25 @@ export function LoginPage() { ) } - function resolveErrorMessage(err: unknown): string | null { - if (!err) return null - if (isAxiosError(err) && err.response?.data) { - const data = err.response.data as { error?: string } - return data.error ?? 'Error al iniciar sesión' - } - return 'Error al iniciar sesión' - } + const errorMessage = resolveErrorMessage(error) return ( -
-
-

- SIG-CM2 -

- -
-
+ + + SIG-CM 2.0 + + Iniciar sesión + + + + {errorMessage && ( + + + {errorMessage} + + )} + + + ) }