diff --git a/app/globals.css b/app/globals.css index 4e1fb75..799b409 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1178,6 +1178,19 @@ body { } .login-submit:hover:not(:disabled) { background: var(--color-primary-hover); } .login-submit:disabled { opacity: 0.6; cursor: not-allowed; } +.login-back-button { margin-top: 16px; } +.login-forgot-link { + align-self: center; + margin-top: 4px; + padding: 4px; + background: none; + border: none; + color: var(--color-text-muted); + font-size: 0.8125rem; + cursor: pointer; + text-decoration: underline; +} +.login-forgot-link:hover { color: var(--color-primary-hover); } /* ── Invite form (/admin/usuarios) ── */ .invite-form { display: flex; flex-direction: column; gap: 12px; } diff --git a/app/login/page.tsx b/app/login/page.tsx index 3fd570d..f5d3d66 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -4,10 +4,14 @@ import { useState } from 'react' import { createBrowserClient } from '@supabase/ssr' import { useRouter } from 'next/navigation' +type Mode = 'login' | 'reset' | 'reset-sent' + export default function LoginPage() { + const [mode, setMode] = useState('login') const [isPending, setIsPending] = useState(false) const [error, setError] = useState(null) const [showPassword, setShowPassword] = useState(false) + const [resetEmail, setResetEmail] = useState('') const router = useRouter() const supabase = createBrowserClient( @@ -36,6 +40,89 @@ export default function LoginPage() { router.refresh() } + // Reusa /auth/confirm: ya escucha PASSWORD_RECOVERY (lo armamos para el + // flujo de invitación, pero Supabase emite el mismo evento acá). + async function handleReset(e: React.FormEvent) { + e.preventDefault() + setIsPending(true) + setError(null) + + const fd = new FormData(e.currentTarget) + const email = String(fd.get('email') ?? '') + + const { error: resetError } = await supabase.auth.resetPasswordForEmail(email, { + redirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/confirm`, + }) + + setIsPending(false) + + if (resetError) { + setError('No pudimos enviar el link. Probá de nuevo.') + return + } + + setResetEmail(email) + setMode('reset-sent') + } + + if (mode === 'reset-sent') { + return ( +
+
+

Revisá tu email

+

+ Si {resetEmail} tiene una cuenta, te enviamos un link para crear una + contraseña nueva. +

+ +
+
+ ) + } + + if (mode === 'reset') { + return ( +
+
+

Recuperar contraseña

+
+
+ + +
+ {error && ( +

{error}

+ )} + + +
+
+
+ ) + } + return (
@@ -96,6 +183,13 @@ export default function LoginPage() { > {isPending ? 'Ingresando…' : 'Ingresar'} +