# US-FUND-007: Navegación y routing **Épica:** EAI-001 - Fundamentos **Sprint:** Mes 1, Semana 2 **Story Points:** 5 SP **Presupuesto:** $1,800 MXN **Prioridad:** Alta (Alcance Inicial) **Estado:** ✅ Completada (Mes 1) --- ## Descripción Como **usuario**, quiero **navegar fluidamente entre las diferentes secciones de la aplicación** para **acceder a las funcionalidades que necesito de manera intuitiva**. **Contexto del Alcance Inicial:** El MVP implementa un sistema de routing básico con React Router, incluyendo rutas protegidas que requieren autenticación y redirección automática según el rol del usuario. La navegación es simple y directa, sin transiciones complejas ni lazy loading avanzado. --- ## Criterios de Aceptación ### Rutas Básicas - [ ] **CA-01:** Rutas públicas accesibles sin autenticación: `/login`, `/register`, `/forgot-password` - [ ] **CA-02:** Rutas protegidas requieren autenticación: `/dashboard`, `/profile`, `/modules/*` - [ ] **CA-03:** Redirección automática a `/login` si usuario no autenticado intenta acceder a ruta protegida - [ ] **CA-04:** Redirección automática a `/dashboard` si usuario autenticado intenta acceder a `/login` - [ ] **CA-05:** Ruta `/` redirige a `/dashboard` si autenticado, o `/login` si no ### Navegación - [ ] **CA-06:** Navbar con links a secciones principales (Dashboard, Módulos, Perfil) - [ ] **CA-07:** Navbar muestra opciones según rol (estudiante vs profesor) - [ ] **CA-08:** Botón de logout funcional en navbar - [ ] **CA-09:** Indicador visual de ruta activa en navbar - [ ] **CA-10:** Breadcrumbs en páginas anidadas (ej: Módulo > Actividad) ### UX - [ ] **CA-11:** Página 404 personalizada para rutas no encontradas - [ ] **CA-12:** Loading state al cambiar de ruta - [ ] **CA-13:** Scroll to top al cambiar de página - [ ] **CA-14:** Navegación con botón "atrás" del navegador funciona correctamente --- ## Especificaciones Técnicas ### Estructura de Rutas **Definición de Rutas:** ```typescript // routes/index.tsx import { createBrowserRouter, Navigate } from 'react-router-dom' import { ProtectedRoute } from './ProtectedRoute' import { PublicRoute } from './PublicRoute' export const router = createBrowserRouter([ // Rutas públicas { path: '/login', element: ( ) }, { path: '/register', element: ( ) }, { path: '/forgot-password', element: ( ) }, { path: '/reset-password/:token', element: ( ) }, // Rutas protegidas { path: '/', element: ( ), children: [ { index: true, element: }, { path: 'dashboard', element: }, { path: 'profile', element: }, { path: 'profile/edit', element: }, { path: 'modules', element: }, { path: 'modules/:moduleId', element: }, { path: 'modules/:moduleId/activities/:activityId', element: }, { path: 'leaderboard', element: }, { path: 'achievements', element: } ] }, // 404 { path: '*', element: } ]) ``` **ProtectedRoute Component:** ```typescript // routes/ProtectedRoute.tsx import { Navigate, useLocation } from 'react-router-dom' import { useAuthStore } from '@/store/auth.store' export function ProtectedRoute({ children }: { children: React.ReactNode }) { const isAuthenticated = useAuthStore(state => state.isAuthenticated) const isLoading = useAuthStore(state => state.isLoading) const location = useLocation() if (isLoading) { return } if (!isAuthenticated) { return } return <>{children} } ``` **PublicRoute Component:** ```typescript // routes/PublicRoute.tsx import { Navigate } from 'react-router-dom' import { useAuthStore } from '@/store/auth.store' export function PublicRoute({ children }: { children: React.ReactNode }) { const isAuthenticated = useAuthStore(state => state.isAuthenticated) if (isAuthenticated) { return } return <>{children} } ``` ### Layout Principal **AppLayout Component:** ```typescript // components/layout/AppLayout.tsx import { Outlet } from 'react-router-dom' import { Navbar } from './Navbar' import { ScrollToTop } from './ScrollToTop' export function AppLayout() { return (
) } ``` **Navbar Component:** ```typescript // components/layout/Navbar.tsx import { NavLink, useNavigate } from 'react-router-dom' import { useAuthStore } from '@/store/auth.store' export function Navbar() { const user = useAuthStore(state => state.user) const logout = useAuthStore(state => state.logout) const navigate = useNavigate() const handleLogout = async () => { await logout() navigate('/login') } return ( ) } ``` **ScrollToTop Component:** ```typescript // components/layout/ScrollToTop.tsx import { useEffect } from 'react' import { useLocation } from 'react-router-dom' export function ScrollToTop() { const { pathname } = useLocation() useEffect(() => { window.scrollTo(0, 0) }, [pathname]) return null } ``` **Breadcrumbs Component:** ```typescript // components/layout/Breadcrumbs.tsx import { Link, useLocation } from 'react-router-dom' export function Breadcrumbs() { const location = useLocation() const pathnames = location.pathname.split('/').filter(x => x) return ( ) } ``` **404 Page:** ```typescript // pages/NotFoundPage.tsx import { Link } from 'react-router-dom' export function NotFoundPage() { return (

404

Página no encontrada

La página que buscas no existe o fue movida.

Volver al Dashboard
) } ``` --- ## Dependencias **Antes:** - US-FUND-001 (Autenticación) - US-FUND-004 (Infraestructura) - US-FUND-005 (Sesiones) **Después:** - Todas las páginas dependen de este sistema de routing --- ## Definición de Hecho (DoD) - [x] React Router configurado - [x] Rutas públicas y protegidas implementadas - [x] Navbar con navegación funcional - [x] Página 404 personalizada - [x] Scroll to top al cambiar ruta - [x] Breadcrumbs en rutas anidadas - [x] Redirecciones automáticas funcionando - [x] Tests de navegación - [x] Responsive navbar (mobile menu) --- ## Notas del Alcance Inicial - ✅ Routing básico con React Router v6 - ✅ Sin lazy loading de rutas - ✅ Sin transiciones animadas entre páginas - ✅ Sin pre-fetching de datos - ✅ Navbar simple sin mega-menu - ⚠️ **Extensión futura:** EXT-014-UX (lazy loading, transiciones, pre-fetching) - ⚠️ **Extensión futura:** EXT-015-Mobile (app nativa con navegación móvil) --- ## Testing ### Tests Unitarios ```typescript describe('ProtectedRoute', () => { it('should redirect to login if not authenticated') it('should render children if authenticated') it('should show loading while checking auth') }) describe('PublicRoute', () => { it('should redirect to dashboard if authenticated') it('should render children if not authenticated') }) ``` ### Tests E2E ```typescript describe('Navigation', () => { it('should navigate to dashboard after login') it('should redirect to login when accessing protected route') it('should show 404 for invalid routes') it('should navigate using navbar links') it('should logout and redirect to login') it('should maintain state after navigation') }) ``` --- ## Estimación **Desglose de Esfuerzo (5 SP = ~2 días):** - React Router setup: 0.5 días - Protected/Public routes: 0.5 días - Navbar + layout: 0.75 días - Breadcrumbs + 404: 0.25 días - Testing: 0.5 días --- **Creado:** 2025-11-02 **Actualizado:** 2025-11-02 **Responsable:** Equipo Frontend