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:
@@ -1,13 +1,13 @@
|
||||
import { lazy, Suspense } from 'react'
|
||||
import { createRouter, createRootRoute, createRoute, Outlet } from '@tanstack/react-router'
|
||||
import { lazy, Suspense, useEffect } from 'react'
|
||||
import { createRouter, createRootRoute, createRoute, Outlet, useNavigate, useRouterState } from '@tanstack/react-router'
|
||||
import { WorkspacePage } from '@/features/workspace/WorkspacePage'
|
||||
import { ImportPage } from '@/features/import/ImportPage'
|
||||
import { LibraryPage } from '@/features/library/LibraryPage'
|
||||
import { SettingsPage } from '@/features/settings/SettingsPage'
|
||||
import { LoginPage } from '@/features/login/LoginPage'
|
||||
import { useAuth } from '@/auth/AuthContext'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
|
||||
// ECharts es grande (373KB gzip). Lazy-load del ReportPage para que el
|
||||
// bundle inicial no lo incluya — se carga solo cuando el usuario llega al reporte.
|
||||
const ReportPage = lazy(() =>
|
||||
import('@/features/report/ReportPage').then((m) => ({ default: m.ReportPage }))
|
||||
)
|
||||
@@ -25,7 +25,40 @@ function ReportPageWithSuspense() {
|
||||
)
|
||||
}
|
||||
|
||||
const rootRoute = createRootRoute({ component: () => <Outlet /> })
|
||||
function RootComponent() {
|
||||
const { user, loading } = useAuth()
|
||||
const navigate = useNavigate()
|
||||
const pathname = useRouterState({ select: (s) => s.location.pathname })
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) return
|
||||
if (!user && pathname !== '/login') {
|
||||
void navigate({ to: '/login' })
|
||||
} else if (user && pathname === '/login') {
|
||||
void navigate({ to: '/' })
|
||||
}
|
||||
}, [user, loading, pathname, navigate])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<Loader2 className="h-8 w-8 animate-spin" style={{ color: '#F59845' }} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!user && pathname !== '/login') return null
|
||||
|
||||
return <Outlet />
|
||||
}
|
||||
|
||||
const rootRoute = createRootRoute({ component: RootComponent })
|
||||
|
||||
const loginRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: '/login',
|
||||
component: LoginPage,
|
||||
})
|
||||
|
||||
const indexRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
@@ -57,7 +90,15 @@ const settingsRoute = createRoute({
|
||||
component: SettingsPage,
|
||||
})
|
||||
|
||||
const routeTree = rootRoute.addChildren([indexRoute, importRoute, workspaceRoute, reportRoute, settingsRoute])
|
||||
const routeTree = rootRoute.addChildren([
|
||||
loginRoute,
|
||||
indexRoute,
|
||||
importRoute,
|
||||
workspaceRoute,
|
||||
reportRoute,
|
||||
settingsRoute,
|
||||
])
|
||||
|
||||
export const router = createRouter({ routeTree })
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
|
||||
Reference in New Issue
Block a user