prometeo-design-system 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -796
- package/dist/components/Dialog/Dialog.d.ts +34 -1
- package/dist/components/Dialog/useDialogControl.d.ts +7 -0
- package/dist/components/Drawer/Drawer.d.ts +12 -0
- package/dist/components/Drawer/useAnimateDrawer.d.ts +2 -0
- package/dist/components/Drawer/useDrawerControls.d.ts +7 -0
- package/dist/components/Layout/LayoutGeneric.d.ts +3 -1
- package/dist/components/TabLinks/TabLinks.d.ts +19 -0
- package/dist/components/TextArea/TextArea.d.ts +14 -0
- package/dist/index.d.ts +6 -1
- package/dist/preview/Drawer.d.ts +2 -0
- package/dist/preview/Select.d.ts +2 -0
- package/dist/preview/Sidebar.d.ts +2 -0
- package/dist/preview/TabLinks.d.ts +2 -0
- package/dist/preview/TextArea.d.ts +2 -0
- package/dist/prometeo-design-system.css +1 -1
- package/dist/prometeo-design-system.es.js +4218 -4007
- package/dist/prometeo-design-system.umd.js +27 -27
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,796 +0,0 @@
|
|
|
1
|
-
# Desing components
|
|
2
|
-
|
|
3
|
-
## Button Component
|
|
4
|
-
|
|
5
|
-
```tsx
|
|
6
|
-
import Button from "./components/Button";
|
|
7
|
-
import type {
|
|
8
|
-
ButtonVariant,
|
|
9
|
-
ButtonColor,
|
|
10
|
-
ButtonSize,
|
|
11
|
-
ButtonState,
|
|
12
|
-
} from "./components/Button";
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
```tsx
|
|
16
|
-
import Button from "./components/Button";
|
|
17
|
-
|
|
18
|
-
function App() {
|
|
19
|
-
return (
|
|
20
|
-
<div>
|
|
21
|
-
{/* Botón básico */}
|
|
22
|
-
<Button label="Click me" onClick={() => console.log("Clicked!")} />
|
|
23
|
-
|
|
24
|
-
{/* Botón con icono */}
|
|
25
|
-
<Button
|
|
26
|
-
label="Guardar"
|
|
27
|
-
icon={<SaveIcon />}
|
|
28
|
-
variant="filled"
|
|
29
|
-
color="primary"
|
|
30
|
-
/>
|
|
31
|
-
|
|
32
|
-
{/* Botón de carga */}
|
|
33
|
-
<Button
|
|
34
|
-
label="Enviando..."
|
|
35
|
-
isLoading={true}
|
|
36
|
-
Spinner={<CustomSpinner />}
|
|
37
|
-
/>
|
|
38
|
-
</div>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## 📋 API Reference
|
|
44
|
-
|
|
45
|
-
### Props
|
|
46
|
-
|
|
47
|
-
| Prop | Tipo | Default | Descripción |
|
|
48
|
-
| ----------------- | --------------------------------- | ----------- | ----------------------------------------------- |
|
|
49
|
-
| `label` | `string` | `undefined` | Texto a mostrar en el botón |
|
|
50
|
-
| `onClick` | `() => void` | `undefined` | Función ejecutada al hacer click |
|
|
51
|
-
| `icon` | `React.ReactNode` | `undefined` | Icono a mostrar (se anima en hover) |
|
|
52
|
-
| `classButton` | `string` | `undefined` | Clases CSS personalizadas para el botón |
|
|
53
|
-
| `classButtonText` | `string` | `undefined` | Clases CSS personalizadas para el texto |
|
|
54
|
-
| `animate` | `boolean` | `true` | Habilita/deshabilita animaciones del botón |
|
|
55
|
-
| `animateIcon` | `boolean` | `true` | Habilita/deshabilita animaciones del icono |
|
|
56
|
-
| `isLoading` | `boolean` | `false` | Muestra estado de carga |
|
|
57
|
-
| `disabled` | `boolean` | `false` | Deshabilita el botón |
|
|
58
|
-
| `type` | `"button" \| "submit" \| "reset"` | `"button"` | Tipo de botón HTML |
|
|
59
|
-
| `variant` | `ButtonVariant` | `"filled"` | Estilo visual del botón |
|
|
60
|
-
| `color` | `ButtonColor` | `"primary"` | Esquema de colores |
|
|
61
|
-
| `size` | `ButtonSize` | `"medium"` | Tamaño del botón |
|
|
62
|
-
| `children` | `React.ReactNode` | `undefined` | Contenido personalizado (anula label e icon) |
|
|
63
|
-
| `Spinner` | `React.ReactNode` | `undefined` | Spinner personalizado para estado loading |
|
|
64
|
-
| `forceState` | `ButtonState` | `undefined` | Fuerza un estado específico (útil para testing) |
|
|
65
|
-
|
|
66
|
-
### Tipos Exportados
|
|
67
|
-
|
|
68
|
-
```tsx
|
|
69
|
-
type ButtonVariant = "filled" | "outline" | "text";
|
|
70
|
-
type ButtonColor = "primary" | "secondary";
|
|
71
|
-
type ButtonSize = "small" | "medium" | "large";
|
|
72
|
-
type ButtonState = "enabled" | "hovered" | "focused" | "pressed" | "disabled";
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Variantes
|
|
76
|
-
|
|
77
|
-
### Filled (Default)
|
|
78
|
-
|
|
79
|
-
Botones con fondo sólido, ideales para acciones primarias.
|
|
80
|
-
|
|
81
|
-
```tsx
|
|
82
|
-
<Button variant="filled" color="primary" label="Primary Filled" />
|
|
83
|
-
<Button variant="filled" color="secondary" label="Secondary Filled" />
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### Outline
|
|
87
|
-
|
|
88
|
-
Botones con bordes y fondo transparente, ideales para acciones secundarias.
|
|
89
|
-
|
|
90
|
-
```tsx
|
|
91
|
-
<Button variant="outline" color="primary" label="Primary Outline" />
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Text
|
|
95
|
-
|
|
96
|
-
Botones solo con texto, ideales para acciones terciarias o enlaces.
|
|
97
|
-
|
|
98
|
-
```tsx
|
|
99
|
-
<Button variant="text" color="primary" label="Primary Text" />
|
|
100
|
-
<Button variant="text" color="secondary" label="Secondary Text" />
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Colores
|
|
104
|
-
|
|
105
|
-
### Primary
|
|
106
|
-
|
|
107
|
-
Color principal usado para acciones importantes.
|
|
108
|
-
|
|
109
|
-
```tsx
|
|
110
|
-
<Button color="primary" label="Primary Action" />
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Secondary
|
|
114
|
-
|
|
115
|
-
Color secundario usado para acciones menos importantes.
|
|
116
|
-
|
|
117
|
-
```tsx
|
|
118
|
-
<Button color="secondary" label="Secondary Action" />
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## Tamaños
|
|
122
|
-
|
|
123
|
-
### Small (40px)
|
|
124
|
-
|
|
125
|
-
```tsx
|
|
126
|
-
<Button size="small" label="Small Button" />
|
|
127
|
-
// Dimensiones: h-[40px] min-w-[123px] px-2 text-sm
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Medium (48px) - Default
|
|
131
|
-
|
|
132
|
-
```tsx
|
|
133
|
-
<Button size="medium" label="Medium Button" />
|
|
134
|
-
// Dimensiones: h-[48px] min-w-[134px] px-2 text-base
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Large (56px)
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
<Button size="large" label="Large Button" />
|
|
141
|
-
// Dimensiones: h-[56px] min-w-[145px] px-4 py-3 text-base
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## ⚡ Animaciones
|
|
145
|
-
|
|
146
|
-
El componente incluye varias animaciones por defecto:
|
|
147
|
-
|
|
148
|
-
### Animaciones del Botón
|
|
149
|
-
|
|
150
|
-
- **Hover**: Escala a 1.05x
|
|
151
|
-
- **Tap**: Escala a 0.98x
|
|
152
|
-
- **Transición**: Spring animation con stiffness: 400, damping: 17
|
|
153
|
-
|
|
154
|
-
### Animaciones del Icono
|
|
155
|
-
|
|
156
|
-
- **Hover**: Rotación de 90 grados (si `animateIcon` está habilitado)
|
|
157
|
-
- **Transición**: Spring animation suave
|
|
158
|
-
|
|
159
|
-
```tsx
|
|
160
|
-
{
|
|
161
|
-
/* Deshabilitar todas las animaciones */
|
|
162
|
-
}
|
|
163
|
-
<Button animate={false} animateIcon={false} />;
|
|
164
|
-
|
|
165
|
-
{
|
|
166
|
-
/* Solo deshabilitar animación del icono */
|
|
167
|
-
}
|
|
168
|
-
<Button animateIcon={false} icon={<Icon />} label="Icono estático" />;
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## 🔄 Estado de Carga
|
|
172
|
-
|
|
173
|
-
```tsx
|
|
174
|
-
const [loading, setLoading] = useState(false);
|
|
175
|
-
|
|
176
|
-
const handleSubmit = async () => {
|
|
177
|
-
setLoading(true);
|
|
178
|
-
try {
|
|
179
|
-
await submitForm();
|
|
180
|
-
} finally {
|
|
181
|
-
setLoading(false);
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
return (
|
|
186
|
-
<Button
|
|
187
|
-
label="Enviar Formulario"
|
|
188
|
-
isLoading={loading}
|
|
189
|
-
onClick={handleSubmit}
|
|
190
|
-
Spinner={<CustomSpinner />} // Spinner personalizado
|
|
191
|
-
/>
|
|
192
|
-
);
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
**Comportamiento del Loading:**
|
|
196
|
-
|
|
197
|
-
- Muestra un spinner (personalizable)
|
|
198
|
-
- En tamaño `small` solo muestra el spinner
|
|
199
|
-
- En tamaños `medium` y `large` muestra spinner + texto "Cargando..."
|
|
200
|
-
- Deshabilita automáticamente el botón
|
|
201
|
-
- Detiene todas las animaciones
|
|
202
|
-
|
|
203
|
-
### Clases Personalizadas
|
|
204
|
-
|
|
205
|
-
```tsx
|
|
206
|
-
<Button
|
|
207
|
-
classButton="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600"
|
|
208
|
-
classButtonText="font-bold text-lg"
|
|
209
|
-
label="Botón Gradient"
|
|
210
|
-
/>
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### Contenido Personalizado Ejemplo
|
|
214
|
-
|
|
215
|
-
```tsx
|
|
216
|
-
<Button onClick={handleClick}>
|
|
217
|
-
<div className="flex items-center gap-3">
|
|
218
|
-
<Avatar src="/user.jpg" size="sm" />
|
|
219
|
-
<div className="text-left">
|
|
220
|
-
<p className="font-semibold">John Doe</p>
|
|
221
|
-
<p className="text-xs opacity-70">Desarrollador</p>
|
|
222
|
-
</div>
|
|
223
|
-
<ChevronDownIcon className="w-4 h-4" />
|
|
224
|
-
</div>
|
|
225
|
-
</Button>
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## Uso Comunes
|
|
229
|
-
|
|
230
|
-
### Botón de Envío de Formulario
|
|
231
|
-
|
|
232
|
-
```tsx
|
|
233
|
-
<Button
|
|
234
|
-
type="submit"
|
|
235
|
-
variant="filled"
|
|
236
|
-
color="primary"
|
|
237
|
-
size="medium"
|
|
238
|
-
label="Enviar"
|
|
239
|
-
isLoading={isSubmitting}
|
|
240
|
-
disabled={!isFormValid}
|
|
241
|
-
/>
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### Botón de Acción con Icono
|
|
245
|
-
|
|
246
|
-
```tsx
|
|
247
|
-
<Button
|
|
248
|
-
label="Descargar PDF"
|
|
249
|
-
icon={<DownloadIcon />}
|
|
250
|
-
variant="outline"
|
|
251
|
-
onClick={downloadPDF}
|
|
252
|
-
/>
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### Botón de Cancelar
|
|
256
|
-
|
|
257
|
-
```tsx
|
|
258
|
-
<Button label="Cancelar" variant="text" color="secondary" onClick={onCancel} />
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
### Botón de Eliminar
|
|
262
|
-
|
|
263
|
-
```tsx
|
|
264
|
-
<Button
|
|
265
|
-
label="Eliminar"
|
|
266
|
-
variant="outline"
|
|
267
|
-
color="primary"
|
|
268
|
-
icon={<TrashIcon />}
|
|
269
|
-
classButton="border-red-500 text-red-500 hover:bg-red-50"
|
|
270
|
-
onClick={handleDelete}
|
|
271
|
-
/>
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
## Sistema de Colores (Design Tokens)
|
|
275
|
-
|
|
276
|
-
El componente utiliza un sistema de design tokens de Tailwind CSS. Los colores se mapean así:
|
|
277
|
-
|
|
278
|
-
### Primary Colors
|
|
279
|
-
|
|
280
|
-
```css
|
|
281
|
-
/* Filled Primary */
|
|
282
|
-
bg-primary-default-default
|
|
283
|
-
text-neutral-strong-default
|
|
284
|
-
hover:bg-primary-default-hover
|
|
285
|
-
|
|
286
|
-
/* Outline Primary */
|
|
287
|
-
border-primary-medium-default
|
|
288
|
-
text-primary-medium-default
|
|
289
|
-
hover:border-primary-medium-hover
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
### Secondary Colors
|
|
293
|
-
|
|
294
|
-
```css
|
|
295
|
-
/* Filled Secondary */
|
|
296
|
-
bg-neutral-strong-medium-default
|
|
297
|
-
text-neutral-strong-default
|
|
298
|
-
|
|
299
|
-
/* Text Secondary */
|
|
300
|
-
text-neutral-strong-default
|
|
301
|
-
hover:text-primary-default-hover
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
---
|
|
305
|
-
|
|
306
|
-
# Sidebar Component
|
|
307
|
-
|
|
308
|
-
Un componente de barra lateral completo y altamente funcional con soporte para navegación, perfil de usuario, gestión de sesiones múltiples, notificaciones y estados colapsables. Diseñado para aplicaciones empresariales con múltiples usuarios y funcionalidades avanzadas.
|
|
309
|
-
|
|
310
|
-
## 🚀 Características Principales
|
|
311
|
-
|
|
312
|
-
- ✅ **Navegación colapsable** con animaciones suaves
|
|
313
|
-
- ✅ **Perfil de usuario** con gestión de sesiones múltiples
|
|
314
|
-
- ✅ **Sistema de notificaciones** con badge
|
|
315
|
-
- ✅ **Logo de empresa** dinámico
|
|
316
|
-
- ✅ **Enlaces de navegación** categorizados (páginas y utilidades)
|
|
317
|
-
- ✅ **Menú contextual** para usuarios en modo colapsado
|
|
318
|
-
- ✅ **Detección de clicks externos** para cerrar menús
|
|
319
|
-
- ✅ **Estados activos** para navegación actual
|
|
320
|
-
- ✅ **Responsive** y accesible
|
|
321
|
-
|
|
322
|
-
## 📦 Dependencias
|
|
323
|
-
|
|
324
|
-
```bash
|
|
325
|
-
npm install react-router-dom framer-motion
|
|
326
|
-
# o
|
|
327
|
-
yarn add react-router-dom framer-motion
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
#### nota: No es necesario instalarlo, seguramente en el template del proyecto ya este instalado.
|
|
331
|
-
|
|
332
|
-
## 📋 Importaciones Requeridas
|
|
333
|
-
|
|
334
|
-
```tsx
|
|
335
|
-
import type { SessionLocalStorage } from "@/interfaces/User/SessionLocalStorage";
|
|
336
|
-
import type { IUser } from "shared-dependencies-tickets";
|
|
337
|
-
import Menu from "../Menu/Menu";
|
|
338
|
-
import { Badge } from "./components/badge";
|
|
339
|
-
import { NavbarCollapseButton } from "./components/collapse-button";
|
|
340
|
-
import { CompanyLogo } from "./components/company-logo";
|
|
341
|
-
import { NavbarLinks } from "./components/nav-links";
|
|
342
|
-
import { UserProfile } from "./components/user-profile";
|
|
343
|
-
import { useNavbarCollapse } from "./hooks/useNavBarCollapse";
|
|
344
|
-
import { useNavbarLinks, type INavLink } from "./hooks/useNavLinks";
|
|
345
|
-
import { useNavbarAnimations } from "./ui/useNavbarAnimation";
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
## 🎯 Uso Básico
|
|
349
|
-
|
|
350
|
-
```tsx
|
|
351
|
-
import Sidebar from "./components/Sidebar";
|
|
352
|
-
|
|
353
|
-
function App() {
|
|
354
|
-
const user = {
|
|
355
|
-
id: 1,
|
|
356
|
-
name: "Juan Pérez",
|
|
357
|
-
email: "juan@empresa.com",
|
|
358
|
-
company_id: {
|
|
359
|
-
name: "Mi Empresa",
|
|
360
|
-
logo: "/logo-empresa.png",
|
|
361
|
-
},
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
const sessions = [
|
|
365
|
-
{ token: "token1", user_id: 1, email: "juan@empresa.com" },
|
|
366
|
-
{ token: "token2", user_id: 2, email: "maria@empresa.com" },
|
|
367
|
-
];
|
|
368
|
-
|
|
369
|
-
const links = {
|
|
370
|
-
pages: [
|
|
371
|
-
{
|
|
372
|
-
id: "dashboard",
|
|
373
|
-
label: "Dashboard",
|
|
374
|
-
path: "/dashboard",
|
|
375
|
-
icon: <DashboardIcon />,
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
id: "projects",
|
|
379
|
-
label: "Proyectos",
|
|
380
|
-
path: "/projects",
|
|
381
|
-
icon: <ProjectIcon />,
|
|
382
|
-
},
|
|
383
|
-
],
|
|
384
|
-
utils: [
|
|
385
|
-
{
|
|
386
|
-
id: "notifications",
|
|
387
|
-
label: "Notificaciones",
|
|
388
|
-
path: "/notifications",
|
|
389
|
-
icon: <BellIcon />,
|
|
390
|
-
},
|
|
391
|
-
],
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
return (
|
|
395
|
-
<div className="flex h-screen">
|
|
396
|
-
<Sidebar
|
|
397
|
-
user={user}
|
|
398
|
-
sessions={sessions}
|
|
399
|
-
handleLogout={(user) => console.log("Logout", user)}
|
|
400
|
-
handleTokenLogin={(token) => console.log("Switch user", token)}
|
|
401
|
-
links={links}
|
|
402
|
-
isActiveModalNotification={false}
|
|
403
|
-
setIsActiveModalNotification={() => {}}
|
|
404
|
-
handleNotificationClick={() => console.log("Notifications")}
|
|
405
|
-
handleProfileClick={() => console.log("Profile")}
|
|
406
|
-
/>
|
|
407
|
-
<main className="flex-1">{/* Contenido principal */}</main>
|
|
408
|
-
</div>
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
## 📋 API Reference
|
|
414
|
-
|
|
415
|
-
### Props Principales
|
|
416
|
-
|
|
417
|
-
| Prop | Tipo | Requerido | Descripción |
|
|
418
|
-
| ------------------------------ | ---------------------------------------- | --------- | ----------------------------------------------------------- |
|
|
419
|
-
| `user` | `IUser` | ✅ | Información del usuario actual |
|
|
420
|
-
| `sessions` | `SessionLocalStorage[]` | ✅ | Lista de sesiones activas disponibles |
|
|
421
|
-
| `handleLogout` | `(user: IUser) => void` | ✅ | Función para cerrar sesión |
|
|
422
|
-
| `handleTokenLogin` | `(token: string) => void` | ✅ | Función para cambiar entre sesiones |
|
|
423
|
-
| `links` | `Record<"pages" \| "utils", INavLink[]>` | ✅ | Enlaces de navegación categorizados |
|
|
424
|
-
| `isActiveModalNotification` | `boolean` | ✅ | Estado del modal de notificaciones |
|
|
425
|
-
| `setIsActiveModalNotification` | `(state: boolean) => void` | ✅ | Función para controlar modal de notificaciones |
|
|
426
|
-
| `handleNotificationClick` | `() => void` | ✅ | Función ejecutada al hacer click en notificaciones |
|
|
427
|
-
| `handleProfileClick` | `() => void` | ❌ | Función ejecutada al hacer click en perfil (modo expandido) |
|
|
428
|
-
|
|
429
|
-
### Tipos de Datos
|
|
430
|
-
|
|
431
|
-
```tsx
|
|
432
|
-
interface IUser {
|
|
433
|
-
id: number;
|
|
434
|
-
name: string;
|
|
435
|
-
email: string;
|
|
436
|
-
company_id: {
|
|
437
|
-
name: string;
|
|
438
|
-
logo: string;
|
|
439
|
-
};
|
|
440
|
-
// ... otros campos del usuario
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
interface SessionLocalStorage {
|
|
444
|
-
token: string;
|
|
445
|
-
user_id: number;
|
|
446
|
-
email: string;
|
|
447
|
-
// ... otros campos de sesión
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
interface INavLink {
|
|
451
|
-
id: string;
|
|
452
|
-
label: string;
|
|
453
|
-
path: string;
|
|
454
|
-
icon: React.ReactNode;
|
|
455
|
-
badge?: number; // Para mostrar contadores
|
|
456
|
-
disabled?: boolean;
|
|
457
|
-
}
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
## 🏗️ Estructura del Componente
|
|
461
|
-
|
|
462
|
-
### Layout Principal
|
|
463
|
-
|
|
464
|
-
```
|
|
465
|
-
┌─────────────────────────┐
|
|
466
|
-
│ [Collapse Button] │
|
|
467
|
-
├─────────────────────────┤
|
|
468
|
-
│ [Company Logo] │
|
|
469
|
-
├─────────────────────────┤
|
|
470
|
-
│ ───────────────── │ ← Separador
|
|
471
|
-
├─────────────────────────┤
|
|
472
|
-
│ │
|
|
473
|
-
│ [Navigation Links] │
|
|
474
|
-
│ • Dashboard │
|
|
475
|
-
│ • Proyectos │
|
|
476
|
-
│ • Reportes │
|
|
477
|
-
│ │
|
|
478
|
-
├─────────────────────────┤ ← Flex Grow
|
|
479
|
-
│ │
|
|
480
|
-
│ [Utility Links] │
|
|
481
|
-
│ • Notificaciones [2] │
|
|
482
|
-
│ • Configuración │
|
|
483
|
-
│ │
|
|
484
|
-
├─────────────────────────┤
|
|
485
|
-
│ ───────────────── │ ← Separador
|
|
486
|
-
├─────────────────────────┤
|
|
487
|
-
│ [User Profile] │
|
|
488
|
-
│ • Avatar + Nombre │
|
|
489
|
-
│ • Sesiones múltiples │
|
|
490
|
-
└─────────────────────────┘
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
### Estados del Sidebar
|
|
494
|
-
|
|
495
|
-
#### 1. **Expandido** (w-64 / 256px)
|
|
496
|
-
|
|
497
|
-
- Muestra todos los elementos con texto
|
|
498
|
-
- Logo completo de la empresa
|
|
499
|
-
- Enlaces con iconos y etiquetas
|
|
500
|
-
- Perfil de usuario completo
|
|
501
|
-
|
|
502
|
-
#### 2. **Colapsado** (w-16 / 64px)
|
|
503
|
-
|
|
504
|
-
- Solo muestra iconos
|
|
505
|
-
- Logo reducido
|
|
506
|
-
- Enlaces solo con iconos
|
|
507
|
-
- Avatar de usuario solamente
|
|
508
|
-
|
|
509
|
-
#### 3. **Colapsado + Menú Contextual**
|
|
510
|
-
|
|
511
|
-
- Sidebar colapsado
|
|
512
|
-
- Menú flotante con opciones de usuario
|
|
513
|
-
- Cambio de sesiones disponible
|
|
514
|
-
|
|
515
|
-
## 🎨 Componentes Internos
|
|
516
|
-
|
|
517
|
-
### NavbarCollapseButton
|
|
518
|
-
|
|
519
|
-
Botón para alternar entre estado expandido y colapsado.
|
|
520
|
-
|
|
521
|
-
```tsx
|
|
522
|
-
<NavbarCollapseButton isCollapsed={isCollapsed} onToggle={toggleCollapse} />
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
### CompanyLogo
|
|
526
|
-
|
|
527
|
-
Muestra el logo y nombre de la empresa del usuario.
|
|
528
|
-
|
|
529
|
-
```tsx
|
|
530
|
-
<CompanyLogo
|
|
531
|
-
logoUrl={user?.company_id.logo || ""}
|
|
532
|
-
companyName={user?.company_id.name}
|
|
533
|
-
/>
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
```tsx
|
|
537
|
-
|
|
538
|
-
<NavbarLinks
|
|
539
|
-
links={pageLinks}
|
|
540
|
-
isLinkActive={isLinkActive}
|
|
541
|
-
/>
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
<NavbarLinks
|
|
545
|
-
links={utilLinks}
|
|
546
|
-
componentBadge={() => <Badge notificationsCount={0} />}
|
|
547
|
-
onClick={handleNotificationClick}
|
|
548
|
-
isLinkActive={isLinkActive}
|
|
549
|
-
activeModal={isActiveModalNotification}
|
|
550
|
-
/>
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
### UserProfile
|
|
554
|
-
|
|
555
|
-
Perfil de usuario con gestión de sesiones múltiples.
|
|
556
|
-
|
|
557
|
-
```tsx
|
|
558
|
-
<UserProfile
|
|
559
|
-
sessions={sessions}
|
|
560
|
-
user={user}
|
|
561
|
-
isExpanded={isExpanded}
|
|
562
|
-
showOptions={true}
|
|
563
|
-
onProfileClick={handleProfileClick}
|
|
564
|
-
onClick={toggleExpandedUserConfig}
|
|
565
|
-
onClickLogout={handleLogout}
|
|
566
|
-
handleTokenLogin={handleTokenLogin}
|
|
567
|
-
/>
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
## Estados
|
|
571
|
-
|
|
572
|
-
### Estados Internos
|
|
573
|
-
|
|
574
|
-
```tsx
|
|
575
|
-
const [isCollapsed, setIsCollapsed] = useState(false); // Sidebar colapsado
|
|
576
|
-
const [isExpanded, setIsExpanded] = useState(false); // Menú de usuario expandido
|
|
577
|
-
const [isExpandedUserConfig, setIsExpandedUserConfig] = useState(false); // Config de usuario
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
### Comportamientos Automáticos
|
|
581
|
-
|
|
582
|
-
1. **Click fuera del menú**: Cierra automáticamente el menú de configuración de usuario
|
|
583
|
-
2. **Colapsar sidebar**: Automáticamente cierra los menús expandidos
|
|
584
|
-
3. **Cambio de sesión**: Cierra el menú de usuario tras cambiar sesión
|
|
585
|
-
|
|
586
|
-
### Lógica de Interacciones
|
|
587
|
-
|
|
588
|
-
```tsx
|
|
589
|
-
// Alternar colapso del sidebar
|
|
590
|
-
const toggleCollapse = () => {
|
|
591
|
-
setIsExpandedUserConfig(false); // Cierra menú de usuario
|
|
592
|
-
setIsCollapsed(!isCollapsed);
|
|
593
|
-
};
|
|
594
|
-
|
|
595
|
-
// Manejar click en perfil (diferentes comportamientos según estado)
|
|
596
|
-
const handleLocalProfileClick = () => {
|
|
597
|
-
setIsExpandedUserConfig(!isExpandedUserConfig);
|
|
598
|
-
};
|
|
599
|
-
|
|
600
|
-
// Click externo para cerrar menú
|
|
601
|
-
useEffect(() => {
|
|
602
|
-
const handleClickOutside = (event: MouseEvent) => {
|
|
603
|
-
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
|
|
604
|
-
setIsExpandedUserConfig(false);
|
|
605
|
-
}
|
|
606
|
-
};
|
|
607
|
-
|
|
608
|
-
if (isExpandedUserConfig) {
|
|
609
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
613
|
-
}, [isExpandedUserConfig]);
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
### Clases CSS Base
|
|
617
|
-
|
|
618
|
-
```tsx
|
|
619
|
-
className={`
|
|
620
|
-
bg-neutral-default-default
|
|
621
|
-
overflow-hidden
|
|
622
|
-
h-full
|
|
623
|
-
flex
|
|
624
|
-
flex-col
|
|
625
|
-
border-r
|
|
626
|
-
border-neutral-strong-default
|
|
627
|
-
transition-all
|
|
628
|
-
duration-300
|
|
629
|
-
${isCollapsed ? 'w-16' : 'w-64'}
|
|
630
|
-
`}
|
|
631
|
-
```
|
|
632
|
-
|
|
633
|
-
### Badges
|
|
634
|
-
|
|
635
|
-
```tsx
|
|
636
|
-
const CustomBadge = ({ count }: { count: number }) => (
|
|
637
|
-
<div className="bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs">
|
|
638
|
-
{count > 99 ? "99+" : count}
|
|
639
|
-
</div>
|
|
640
|
-
);
|
|
641
|
-
|
|
642
|
-
<NavbarLinks
|
|
643
|
-
links={utilLinks}
|
|
644
|
-
componentBadge={() => <CustomBadge count={notificationCount} />}
|
|
645
|
-
// ... otras props
|
|
646
|
-
/>;
|
|
647
|
-
```
|
|
648
|
-
|
|
649
|
-
### Enlaces Dinámicos por Rol
|
|
650
|
-
|
|
651
|
-
```tsx
|
|
652
|
-
const getLinksForUser = (user: IUser) => {
|
|
653
|
-
const baseLinks = [
|
|
654
|
-
{
|
|
655
|
-
id: "dashboard",
|
|
656
|
-
label: "Dashboard",
|
|
657
|
-
path: "/dashboard",
|
|
658
|
-
icon: <HomeIcon />,
|
|
659
|
-
},
|
|
660
|
-
];
|
|
661
|
-
|
|
662
|
-
if (user.role === "admin") {
|
|
663
|
-
baseLinks.push(
|
|
664
|
-
{ id: "users", label: "Usuarios", path: "/users", icon: <UsersIcon /> },
|
|
665
|
-
{
|
|
666
|
-
id: "settings",
|
|
667
|
-
label: "Configuración",
|
|
668
|
-
path: "/settings",
|
|
669
|
-
icon: <SettingsIcon />,
|
|
670
|
-
}
|
|
671
|
-
);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
return { pages: baseLinks, utils: [] };
|
|
675
|
-
};
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
## Ejemplo de uso
|
|
679
|
-
|
|
680
|
-
### Navbar completo
|
|
681
|
-
|
|
682
|
-
```tsx
|
|
683
|
-
import { useState, useEffect } from "react";
|
|
684
|
-
import { useNavigate, useLocation } from "react-router-dom";
|
|
685
|
-
import Sidebar from "./components/Sidebar";
|
|
686
|
-
|
|
687
|
-
function Navbar() {
|
|
688
|
-
const navigate = useNavigate();
|
|
689
|
-
const location = useLocation();
|
|
690
|
-
const [user, setUser] = useState(null);
|
|
691
|
-
const [sessions, setSessions] = useState([]);
|
|
692
|
-
const [notificationCount, setNotificationCount] = useState(0);
|
|
693
|
-
const [showNotifications, setShowNotifications] = useState(false);
|
|
694
|
-
|
|
695
|
-
const links = {
|
|
696
|
-
pages: [
|
|
697
|
-
{
|
|
698
|
-
id: "dashboard",
|
|
699
|
-
label: "Dashboard",
|
|
700
|
-
path: "/dashboard",
|
|
701
|
-
icon: <HomeIcon className="w-5 h-5" />,
|
|
702
|
-
},
|
|
703
|
-
{
|
|
704
|
-
id: "tickets",
|
|
705
|
-
label: "Tickets",
|
|
706
|
-
path: "/tickets",
|
|
707
|
-
icon: <TicketIcon className="w-5 h-5" />,
|
|
708
|
-
},
|
|
709
|
-
{
|
|
710
|
-
id: "projects",
|
|
711
|
-
label: "Proyectos",
|
|
712
|
-
path: "/projects",
|
|
713
|
-
icon: <FolderIcon className="w-5 h-5" />,
|
|
714
|
-
},
|
|
715
|
-
{
|
|
716
|
-
id: "reports",
|
|
717
|
-
label: "Reportes",
|
|
718
|
-
path: "/reports",
|
|
719
|
-
icon: <ChartIcon className="w-5 h-5" />,
|
|
720
|
-
},
|
|
721
|
-
],
|
|
722
|
-
utils: [
|
|
723
|
-
{
|
|
724
|
-
id: "notifications",
|
|
725
|
-
label: "Notificaciones",
|
|
726
|
-
path: "/notifications",
|
|
727
|
-
icon: <BellIcon className="w-5 h-5" />,
|
|
728
|
-
badge: notificationCount,
|
|
729
|
-
},
|
|
730
|
-
{
|
|
731
|
-
id: "settings",
|
|
732
|
-
label: "Configuración",
|
|
733
|
-
path: "/settings",
|
|
734
|
-
icon: <SettingsIcon className="w-5 h-5" />,
|
|
735
|
-
},
|
|
736
|
-
],
|
|
737
|
-
};
|
|
738
|
-
|
|
739
|
-
const handleLogout = async (user) => {
|
|
740
|
-
try {
|
|
741
|
-
await logoutUser(user);
|
|
742
|
-
navigate("/login");
|
|
743
|
-
} catch (error) {
|
|
744
|
-
console.error("Error al cerrar sesión:", error);
|
|
745
|
-
}
|
|
746
|
-
};
|
|
747
|
-
|
|
748
|
-
const handleTokenLogin = async (token) => {
|
|
749
|
-
try {
|
|
750
|
-
const newUser = await switchUserSession(token);
|
|
751
|
-
setUser(newUser);
|
|
752
|
-
navigate("/dashboard");
|
|
753
|
-
} catch (error) {
|
|
754
|
-
console.error("Error al cambiar sesión:", error);
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
const handleNotificationClick = () => {
|
|
759
|
-
setShowNotifications(true);
|
|
760
|
-
setNotificationCount(0);
|
|
761
|
-
};
|
|
762
|
-
|
|
763
|
-
return (
|
|
764
|
-
<div className="flex h-screen bg-gray-50">
|
|
765
|
-
<Sidebar
|
|
766
|
-
user={user}
|
|
767
|
-
sessions={sessions}
|
|
768
|
-
handleLogout={handleLogout}
|
|
769
|
-
handleTokenLogin={handleTokenLogin}
|
|
770
|
-
links={links}
|
|
771
|
-
isActiveModalNotification={showNotifications}
|
|
772
|
-
setIsActiveModalNotification={setShowNotifications}
|
|
773
|
-
handleNotificationClick={handleNotificationClick}
|
|
774
|
-
handleProfileClick={() => navigate("/profile")}
|
|
775
|
-
/>
|
|
776
|
-
</div>
|
|
777
|
-
);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
export default Navbar;
|
|
781
|
-
|
|
782
|
-
//<!--- En otro lugar de nuestra app>
|
|
783
|
-
|
|
784
|
-
/* REACT ROUTER */
|
|
785
|
-
<main className="flex-1 overflow-auto p-6">
|
|
786
|
-
<Routes>
|
|
787
|
-
<Route path="/dashboard" element={<DashboardContent />} />
|
|
788
|
-
<Route path="/tickets" element={<TicketsContent />} />
|
|
789
|
-
<Route path="/projects" element={<ProjectsContent />} />
|
|
790
|
-
<Route path="/reports" element={<ReportsContent />} />
|
|
791
|
-
<Route path="/notifications" element={<NotificationsContent />} />
|
|
792
|
-
<Route path="/settings" element={<SettingsContent />} />
|
|
793
|
-
<Route path="/profile" element={<ProfileContent />} />
|
|
794
|
-
</Routes>
|
|
795
|
-
</main>;
|
|
796
|
-
```
|