Fase 2: 04_sanitize.py - scrubbing de secretos de skills/agentes/plans (10 secretos unicos, gate en verde)
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
# 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.2–4.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`.
|
||||
Reference in New Issue
Block a user