Files
qwen3-6-lora/data/raw/sanitized/plans/quiero-empezar-a-planificar-structured-blossom.md
T

5.4 KiB
Raw Blame History

Plan: Etapa 4.1 — Navbar con menú lateral para Dashboard

Contexto

El Dashboard de Ro-ut actualmente es un <h1>Dashboard</h1> sin navegación. La tarea 4.1 del plan de Etapa 4 ("Frontend: Dashboard funcional y gestión de usuarios") requiere integrar el componente Navbar del design system @aleleba/ro-ut-ui para dar al usuario una barra de navegación funcional con sidebar lateral.

El Navbar del UI kit es chrome-only: no envuelve el contenido de la página, sino que se coloca fuera y publica variables CSS (--ro-ut-sidebar-width, --ro-ut-topbar-height) para que el contenido reserve el espacio adecuado.

Archivos a crear

1. src/frontend/components/Dashboard/DashboardLayout/components/DashboardLayout.tsx

Componente layout que envuelve el Dashboard con el Navbar. Sigue el patrón connect de Redux del proyecto:

import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { useNavigate } from 'react-router';
import { Navbar, TopNavbar, LeftNavbar, NavList, NavItem, IconNames } from '@aleleba/ro-ut-ui';
import { logInActions, ILogInPayload } from '@actions';

const { logInAction } = logInActions;

export type TDashboardLayoutProps = {
  children: React.ReactNode;
  setLogIn: ({ payload }: { payload: ILogInPayload }) => void;
  connection: boolean;
  token: string;
  user: string | null;
};

const DashboardLayout = (props: TDashboardLayoutProps) => {
  const { children, setLogIn, connection, token, user } = props;
  const navigate = useNavigate();

  // Auth redirect — mismo patrón que el Dashboard actual
  useEffect(() => {
    if ((connection === false) && (token === null)) {
      navigate('/login');
    }
  }, [connection, token]);

  const handleLogout = () => {
    setLogIn({ payload: { connection: false, token: null, user: null } });
  };

  const navItems = [
    { label: 'Dashboard', to: '/dashboard', icon: IconNames.dashboard, active: true }
  ];

  return (
    <Navbar defaultExpanded>
      <TopNavbar
        brand={<img src="/assets/img/logo-ro-ut.svg" alt="RO-UT Logo" style={{ height: '32px' }} />}
        actions={
          <NavItem label={user || 'Usuario'} onClick={handleLogout} icon={IconNames.usuarioDentroApp} />
        }
      />
      <LeftNavbar>
        <NavList>
          {navItems.map(item => (
            <NavItem key={item.to} label={item.label} to={item.to} icon={item.icon} active={item.active} />
          ))}
        </NavList>
      </LeftNavbar>
      <div className="dashboard-content">{children}</div>
    </Navbar>
  );
};

const mapStateToProps = (state) => ({
  connection: state.logIn.connection,
  token: state.logIn.token,
  user: state.logIn.user
});

const mapDispatchToProps = (dispatch) => ({
  setLogIn: ({ payload }) => dispatch(logInAction(payload))
});

export default connect(mapStateToProps, mapDispatchToProps)(DashboardLayout);

Decisiones clave:

  • children como prop hace el layout composable — cualquier vista futura se envuelve igual.
  • El logout es local (dispatch Redux con connection: false, token: null). Si en el futuro se necesita invalidar sesión en el servidor, se crea un container con GraphQL mutation siguiendo el patrón de LogIn/containers/queryGraphQLLogOut.ts.
  • El auth redirect se mueve del Dashboard al layout, centralizando la lógica de protección.

Archivos a modificar

2. src/frontend/components/Dashboard/Dashboard/components/Dashboard.tsx

Simplificar a componente de contenido puro — sin Redux, sin useEffect, sin useNavigate:

import React from 'react';

const Dashboard = () => (
  <div className="container">
    <div className="row">
      <h1 style={{ color: '#000' }}>Dashboard</h1>
    </div>
  </div>
);

export default Dashboard;

3. src/routes/index.tsx

Envolver el Dashboard con el layout:

import DashboardLayout from '../frontend/components/Dashboard/DashboardLayout/components/DashboardLayout';

const DASHBOARD = {
  path: '/dashboard',
  element: (
    <DashboardLayout>
      <Dashboard />
    </DashboardLayout>
  ),
};

CSS para el offset del contenido

El Navbar publica variables CSS en document.documentElement (runtime, vía JS). El .dashboard-content debe usarlas:

.dashboard-content {
  margin-left: var(--ro-ut-sidebar-width, 0px);
  margin-top: var(--ro-ut-topbar-height, 0px);
  padding: 1rem;
}

Esto asegura que el contenido se ajuste automáticamente cuando el sidebar se colapsa/expande, sin valores hardcodeados.

Comportamiento responsive

El Navbar maneja la responsividad internamente:

  • Desktop: sidebar visible (expandido o colapsado), topbar fija arriba.
  • Móvil: menú hamburguesa en la topbar; sidebar se superpone como acordeón.

No se necesita CSS responsive adicional del layout.

Items de navegación

Por ahora solo Dashboard (/dashboard). El array navItems en el componente es el lugar central para agregar nuevos items cuando se construyan las vistas de usuarios, empresas, etc. (tareas 4.24.5).

Verificación

  1. npm run build — compilar sin errores.
  2. Navegar a /dashboard — ver Navbar con sidebar izquierdo y topbar con logo + nombre de usuario.
  3. Click en logout — redirigir a /login.
  4. Sidebar colapsar/expandir — el contenido se ajusta con las variables CSS.
  5. Navegar a /login con sesión activa — redirigir a /dashboard.
  6. Navegar directamente a /dashboard sin autenticar — redirigir a /login.