feat(sprint-4/etapa-1): Supabase auth + Google OAuth + LoginPage + rutas protegidas
- Cliente Supabase singleton en src/lib/supabase.ts - Interfaces AuthUser / AuthService en src/auth/types.ts - SupabaseAuthService: signInWithGoogle, signOut, getCurrentUser, onAuthStateChange con upsert a tabla users (platform_admin para @inquality.com.py, guest resto) - AuthProvider (React Context) wrappea toda la app; expone user, loading, signIn, signOut - LoginPage /login: logo InQuality color, título, fade-in, botón Google naranja #F59845 - RootComponent del router guarda todas las rutas — sin sesión → /login, con sesión + /login → / - Script SQL: supabase/migrations/001_create_users.sql (tabla users + RLS) - AppHeader: <Link> → <a href="/"> para evitar dependencia de RouterContext en tests - 561/561 tests verdes, build sin errores TS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
57
src/features/login/LoginPage.tsx
Normal file
57
src/features/login/LoginPage.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { useAuth } from '@/auth/AuthContext'
|
||||
|
||||
function GoogleIcon() {
|
||||
return (
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908C16.658 14.013 17.64 11.79 17.64 9.2z" fill="white"/>
|
||||
<path d="M9 18c2.43 0 4.467-.806 5.956-2.184l-2.908-2.258c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332C2.438 15.983 5.482 18 9 18z" fill="white"/>
|
||||
<path d="M3.964 10.707A5.41 5.41 0 0 1 3.682 9c0-.59.102-1.167.282-1.707V4.961H.957C.347 6.175 0 7.55 0 9s.348 2.826.957 4.039l3.007-2.332z" fill="white"/>
|
||||
<path d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0 5.482 0 2.438 2.017.957 4.961L3.964 6.293C4.672 4.169 6.656 3.58 9 3.58z" fill="white"/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function LoginPage() {
|
||||
const { signIn, loading } = useAuth()
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white flex items-center justify-center">
|
||||
<div
|
||||
className="flex flex-col items-center gap-6 px-8"
|
||||
style={{ animation: 'loginFadeIn 0.5s ease-out both' }}
|
||||
>
|
||||
<style>{`
|
||||
@keyframes loginFadeIn {
|
||||
from { opacity: 0; transform: translateY(12px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<img
|
||||
src="/inquality-logo-color.png"
|
||||
alt="InQuality"
|
||||
className="h-12 object-contain"
|
||||
/>
|
||||
|
||||
<div className="text-center">
|
||||
<h1 className="text-3xl font-bold text-slate-900 tracking-tight">InQ ROI</h1>
|
||||
<p className="text-slate-500 mt-1 text-sm">Análisis de procesos y ROI de automatización</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={signIn}
|
||||
disabled={loading}
|
||||
className="flex items-center gap-3 px-6 py-3 rounded-xl font-semibold text-white text-sm shadow-sm transition-opacity hover:opacity-90 disabled:opacity-60 disabled:cursor-not-allowed"
|
||||
style={{ backgroundColor: '#F59845' }}
|
||||
>
|
||||
<GoogleIcon />
|
||||
Iniciar sesión con Google
|
||||
</button>
|
||||
|
||||
<p className="text-xs text-slate-400">
|
||||
Solo para usuarios con cuenta @inquality.com.py
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user