diff --git a/apps/web/app/(auth)/layout.tsx b/apps/web/app/(auth)/layout.tsx
new file mode 100644
index 0000000..37e5241
--- /dev/null
+++ b/apps/web/app/(auth)/layout.tsx
@@ -0,0 +1,31 @@
+import { AuthProvider } from '@/components/providers/auth-provider';
+
+export default function AuthLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+ {/* Decorative pattern background */}
+
+
+ {/* Decorative shapes */}
+
+
+
+ {/* Content */}
+
{children}
+
+
+ );
+}
diff --git a/apps/web/app/(auth)/login/page.tsx b/apps/web/app/(auth)/login/page.tsx
new file mode 100644
index 0000000..4cff09e
--- /dev/null
+++ b/apps/web/app/(auth)/login/page.tsx
@@ -0,0 +1,152 @@
+'use client';
+
+import { Suspense } from 'react';
+import { LoginForm } from '@/components/auth/login-form';
+
+function LoginContent() {
+ return (
+
+ {/* Left side - Branding (hidden on mobile) */}
+
+
+ {/* Logo */}
+
+
+
+
+
+
+ {/* Title */}
+
Padel Pro
+
+ {/* Tagline */}
+
+ Sistema de Gestion para Clubes de Padel
+
+
+ {/* Features */}
+
+
+
+
+
Gestion de Reservas
+
Administra tus canchas y horarios
+
+
+
+
+
+
+
Control de Clientes
+
Membresias y perfiles completos
+
+
+
+
+
+
+
Reportes y Estadisticas
+
Analiza el rendimiento de tu club
+
+
+
+
+
+
+ {/* Right side - Login Form */}
+
+ {/* Mobile Logo */}
+
+
+
+
+
Padel Pro
+
Sistema de Gestion para Clubes de Padel
+
+
+
+
+ {/* Footer */}
+
+ © {new Date().getFullYear()} Padel Pro. Todos los derechos reservados.
+
+
+
+ );
+}
+
+export default function LoginPage() {
+ return (
+
+
+
+ }
+ >
+
+
+ );
+}
diff --git a/apps/web/components/auth/login-form.tsx b/apps/web/components/auth/login-form.tsx
new file mode 100644
index 0000000..d88854f
--- /dev/null
+++ b/apps/web/components/auth/login-form.tsx
@@ -0,0 +1,252 @@
+'use client';
+
+import { useState } from 'react';
+import { signIn } from 'next-auth/react';
+import { useRouter, useSearchParams } from 'next/navigation';
+import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
+import { cn } from '@/lib/utils';
+
+interface LoginFormProps {
+ className?: string;
+}
+
+export function LoginForm({ className }: LoginFormProps) {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+ const callbackUrl = searchParams.get('callbackUrl') || '/dashboard';
+
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [showPassword, setShowPassword] = useState(false);
+ const [rememberMe, setRememberMe] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [errors, setErrors] = useState<{ email?: string; password?: string }>({});
+
+ const validateForm = (): boolean => {
+ const newErrors: { email?: string; password?: string } = {};
+
+ if (!email) {
+ newErrors.email = 'El correo electrónico es requerido';
+ } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
+ newErrors.email = 'Ingresa un correo electrónico válido';
+ }
+
+ if (!password) {
+ newErrors.password = 'La contraseña es requerida';
+ } else if (password.length < 6) {
+ newErrors.password = 'La contraseña debe tener al menos 6 caracteres';
+ }
+
+ setErrors(newErrors);
+ return Object.keys(newErrors).length === 0;
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setError(null);
+
+ if (!validateForm()) {
+ return;
+ }
+
+ setIsLoading(true);
+
+ try {
+ const result = await signIn('credentials', {
+ email,
+ password,
+ redirect: false,
+ });
+
+ if (result?.error) {
+ setError('Credenciales inválidas. Por favor, verifica tu correo y contraseña.');
+ } else {
+ router.push(callbackUrl);
+ router.refresh();
+ }
+ } catch (err) {
+ setError('Ocurrió un error al iniciar sesión. Por favor, intenta de nuevo.');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+
+
+ Iniciar Sesión
+
+ Ingresa tus credenciales para acceder al sistema
+
+
+
+
+
+
+ );
+}
+
+export default LoginForm;