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

160 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```tsx
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:
```tsx
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:
```tsx
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:
```css
.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`.