prometeo-design-system 1.0.0

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 ADDED
@@ -0,0 +1,801 @@
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
+ ## 📋 Importaciones Requeridas
331
+
332
+ ```tsx
333
+ import type { SessionLocalStorage } from "@/interfaces/User/SessionLocalStorage";
334
+ import type { IUser } from "shared-dependencies-tickets";
335
+ import Menu from "../Menu/Menu";
336
+ import { Badge } from "./components/badge";
337
+ import { NavbarCollapseButton } from "./components/collapse-button";
338
+ import { CompanyLogo } from "./components/company-logo";
339
+ import { NavbarLinks } from "./components/nav-links";
340
+ import { UserProfile } from "./components/user-profile";
341
+ import { useNavbarCollapse } from "./hooks/useNavBarCollapse";
342
+ import { useNavbarLinks, type INavLink } from "./hooks/useNavLinks";
343
+ import { useNavbarAnimations } from "./ui/useNavbarAnimation";
344
+ ```
345
+
346
+ ## 🎯 Uso Básico
347
+
348
+ ```tsx
349
+ import Sidebar from "./components/Sidebar";
350
+
351
+ function App() {
352
+ const user = {
353
+ id: 1,
354
+ name: "Juan Pérez",
355
+ email: "juan@empresa.com",
356
+ company_id: {
357
+ name: "Mi Empresa",
358
+ logo: "/logo-empresa.png",
359
+ },
360
+ };
361
+
362
+ const sessions = [
363
+ { token: "token1", user_id: 1, email: "juan@empresa.com" },
364
+ { token: "token2", user_id: 2, email: "maria@empresa.com" },
365
+ ];
366
+
367
+ const links = {
368
+ pages: [
369
+ {
370
+ id: "dashboard",
371
+ label: "Dashboard",
372
+ path: "/dashboard",
373
+ icon: <DashboardIcon />,
374
+ },
375
+ {
376
+ id: "projects",
377
+ label: "Proyectos",
378
+ path: "/projects",
379
+ icon: <ProjectIcon />,
380
+ },
381
+ ],
382
+ utils: [
383
+ {
384
+ id: "notifications",
385
+ label: "Notificaciones",
386
+ path: "/notifications",
387
+ icon: <BellIcon />,
388
+ },
389
+ ],
390
+ };
391
+
392
+ return (
393
+ <div className="flex h-screen">
394
+ <Sidebar
395
+ user={user}
396
+ sessions={sessions}
397
+ handleLogout={(user) => console.log("Logout", user)}
398
+ handleTokenLogin={(token) => console.log("Switch user", token)}
399
+ links={links}
400
+ isActiveModalNotification={false}
401
+ setIsActiveModalNotification={() => {}}
402
+ handleNotificationClick={() => console.log("Notifications")}
403
+ handleProfileClick={() => console.log("Profile")}
404
+ />
405
+ <main className="flex-1">{/* Contenido principal */}</main>
406
+ </div>
407
+ );
408
+ }
409
+ ```
410
+
411
+ ## 📋 API Reference
412
+
413
+ ### Props Principales
414
+
415
+ | Prop | Tipo | Requerido | Descripción |
416
+ | ------------------------------ | ---------------------------------------- | --------- | ----------------------------------------------------------- |
417
+ | `user` | `IUser` | ✅ | Información del usuario actual |
418
+ | `sessions` | `SessionLocalStorage[]` | ✅ | Lista de sesiones activas disponibles |
419
+ | `handleLogout` | `(user: IUser) => void` | ✅ | Función para cerrar sesión |
420
+ | `handleTokenLogin` | `(token: string) => void` | ✅ | Función para cambiar entre sesiones |
421
+ | `links` | `Record<"pages" \| "utils", INavLink[]>` | ✅ | Enlaces de navegación categorizados |
422
+ | `isActiveModalNotification` | `boolean` | ✅ | Estado del modal de notificaciones |
423
+ | `setIsActiveModalNotification` | `(state: boolean) => void` | ✅ | Función para controlar modal de notificaciones |
424
+ | `handleNotificationClick` | `() => void` | ✅ | Función ejecutada al hacer click en notificaciones |
425
+ | `handleProfileClick` | `() => void` | ❌ | Función ejecutada al hacer click en perfil (modo expandido) |
426
+
427
+ ### Tipos de Datos
428
+
429
+ ```tsx
430
+ interface IUser {
431
+ id: number;
432
+ name: string;
433
+ email: string;
434
+ company_id: {
435
+ name: string;
436
+ logo: string;
437
+ };
438
+ // ... otros campos del usuario
439
+ }
440
+
441
+ interface SessionLocalStorage {
442
+ token: string;
443
+ user_id: number;
444
+ email: string;
445
+ // ... otros campos de sesión
446
+ }
447
+
448
+ interface INavLink {
449
+ id: string;
450
+ label: string;
451
+ path: string;
452
+ icon: React.ReactNode;
453
+ badge?: number; // Para mostrar contadores
454
+ disabled?: boolean;
455
+ }
456
+ ```
457
+
458
+ ## 🏗️ Estructura del Componente
459
+
460
+ ### Layout Principal
461
+
462
+ ```
463
+ ┌─────────────────────────┐
464
+ │ [Collapse Button] │
465
+ ├─────────────────────────┤
466
+ │ [Company Logo] │
467
+ ├─────────────────────────┤
468
+ │ ───────────────── │ ← Separador
469
+ ├─────────────────────────┤
470
+ │ │
471
+ │ [Navigation Links] │
472
+ │ • Dashboard │
473
+ │ • Proyectos │
474
+ │ • Reportes │
475
+ │ │
476
+ ├─────────────────────────┤ ← Flex Grow
477
+ │ │
478
+ │ [Utility Links] │
479
+ │ • Notificaciones [2] │
480
+ │ • Configuración │
481
+ │ │
482
+ ├─────────────────────────┤
483
+ │ ───────────────── │ ← Separador
484
+ ├─────────────────────────┤
485
+ │ [User Profile] │
486
+ │ • Avatar + Nombre │
487
+ │ • Sesiones múltiples │
488
+ └─────────────────────────┘
489
+ ```
490
+
491
+ ### Estados del Sidebar
492
+
493
+ #### 1. **Expandido** (w-64 / 256px)
494
+
495
+ - Muestra todos los elementos con texto
496
+ - Logo completo de la empresa
497
+ - Enlaces con iconos y etiquetas
498
+ - Perfil de usuario completo
499
+
500
+ #### 2. **Colapsado** (w-16 / 64px)
501
+
502
+ - Solo muestra iconos
503
+ - Logo reducido
504
+ - Enlaces solo con iconos
505
+ - Avatar de usuario solamente
506
+
507
+ #### 3. **Colapsado + Menú Contextual**
508
+
509
+ - Sidebar colapsado
510
+ - Menú flotante con opciones de usuario
511
+ - Cambio de sesiones disponible
512
+
513
+ ## 🎨 Componentes Internos
514
+
515
+ ### NavbarCollapseButton
516
+
517
+ Botón para alternar entre estado expandido y colapsado.
518
+
519
+ ```tsx
520
+ <NavbarCollapseButton isCollapsed={isCollapsed} onToggle={toggleCollapse} />
521
+ ```
522
+
523
+ ### CompanyLogo
524
+
525
+ Muestra el logo y nombre de la empresa del usuario.
526
+
527
+ ```tsx
528
+ <CompanyLogo
529
+ logoUrl={user?.company_id.logo || ""}
530
+ companyName={user?.company_id.name}
531
+ />
532
+ ```
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
+
571
+
572
+ ## Estados
573
+
574
+ ### Estados Internos
575
+
576
+ ```tsx
577
+ const [isCollapsed, setIsCollapsed] = useState(false); // Sidebar colapsado
578
+ const [isExpanded, setIsExpanded] = useState(false); // Menú de usuario expandido
579
+ const [isExpandedUserConfig, setIsExpandedUserConfig] = useState(false); // Config de usuario
580
+ ```
581
+
582
+ ### Comportamientos Automáticos
583
+
584
+ 1. **Click fuera del menú**: Cierra automáticamente el menú de configuración de usuario
585
+ 2. **Colapsar sidebar**: Automáticamente cierra los menús expandidos
586
+ 3. **Cambio de sesión**: Cierra el menú de usuario tras cambiar sesión
587
+
588
+ ### Lógica de Interacciones
589
+
590
+ ```tsx
591
+ // Alternar colapso del sidebar
592
+ const toggleCollapse = () => {
593
+ setIsExpandedUserConfig(false); // Cierra menú de usuario
594
+ setIsCollapsed(!isCollapsed);
595
+ };
596
+
597
+ // Manejar click en perfil (diferentes comportamientos según estado)
598
+ const handleLocalProfileClick = () => {
599
+ setIsExpandedUserConfig(!isExpandedUserConfig);
600
+ };
601
+
602
+ // Click externo para cerrar menú
603
+ useEffect(() => {
604
+ const handleClickOutside = (event: MouseEvent) => {
605
+ if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
606
+ setIsExpandedUserConfig(false);
607
+ }
608
+ };
609
+
610
+ if (isExpandedUserConfig) {
611
+ document.addEventListener("mousedown", handleClickOutside);
612
+ }
613
+
614
+ return () => document.removeEventListener("mousedown", handleClickOutside);
615
+ }, [isExpandedUserConfig]);
616
+ ```
617
+
618
+ ### Clases CSS Base
619
+
620
+ ```tsx
621
+ className={`
622
+ bg-neutral-default-default
623
+ overflow-hidden
624
+ h-full
625
+ flex
626
+ flex-col
627
+ border-r
628
+ border-neutral-strong-default
629
+ transition-all
630
+ duration-300
631
+ ${isCollapsed ? 'w-16' : 'w-64'}
632
+ `}
633
+ ```
634
+
635
+ ### Badges
636
+
637
+ ```tsx
638
+ const CustomBadge = ({ count }: { count: number }) => (
639
+ <div className="bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs">
640
+ {count > 99 ? "99+" : count}
641
+ </div>
642
+ );
643
+
644
+ <NavbarLinks
645
+ links={utilLinks}
646
+ componentBadge={() => <CustomBadge count={notificationCount} />}
647
+ // ... otras props
648
+ />;
649
+ ```
650
+
651
+ ### Enlaces Dinámicos por Rol
652
+
653
+ ```tsx
654
+ const getLinksForUser = (user: IUser) => {
655
+ const baseLinks = [
656
+ {
657
+ id: "dashboard",
658
+ label: "Dashboard",
659
+ path: "/dashboard",
660
+ icon: <HomeIcon />,
661
+ },
662
+ ];
663
+
664
+ if (user.role === "admin") {
665
+ baseLinks.push(
666
+ { id: "users", label: "Usuarios", path: "/users", icon: <UsersIcon /> },
667
+ {
668
+ id: "settings",
669
+ label: "Configuración",
670
+ path: "/settings",
671
+ icon: <SettingsIcon />,
672
+ }
673
+ );
674
+ }
675
+
676
+ return { pages: baseLinks, utils: [] };
677
+ };
678
+ ```
679
+
680
+
681
+
682
+ ## Ejemplo de uso
683
+
684
+ ### Navbar completo
685
+
686
+ ```tsx
687
+ import { useState, useEffect } from "react";
688
+ import { useNavigate, useLocation } from "react-router-dom";
689
+ import Sidebar from "./components/Sidebar";
690
+
691
+ function Navbar() {
692
+ const navigate = useNavigate();
693
+ const location = useLocation();
694
+ const [user, setUser] = useState(null);
695
+ const [sessions, setSessions] = useState([]);
696
+ const [notificationCount, setNotificationCount] = useState(0);
697
+ const [showNotifications, setShowNotifications] = useState(false);
698
+
699
+ const links = {
700
+ pages: [
701
+ {
702
+ id: "dashboard",
703
+ label: "Dashboard",
704
+ path: "/dashboard",
705
+ icon: <HomeIcon className="w-5 h-5" />,
706
+ },
707
+ {
708
+ id: "tickets",
709
+ label: "Tickets",
710
+ path: "/tickets",
711
+ icon: <TicketIcon className="w-5 h-5" />,
712
+ },
713
+ {
714
+ id: "projects",
715
+ label: "Proyectos",
716
+ path: "/projects",
717
+ icon: <FolderIcon className="w-5 h-5" />,
718
+ },
719
+ {
720
+ id: "reports",
721
+ label: "Reportes",
722
+ path: "/reports",
723
+ icon: <ChartIcon className="w-5 h-5" />,
724
+ },
725
+ ],
726
+ utils: [
727
+ {
728
+ id: "notifications",
729
+ label: "Notificaciones",
730
+ path: "/notifications",
731
+ icon: <BellIcon className="w-5 h-5" />,
732
+ badge: notificationCount,
733
+ },
734
+ {
735
+ id: "settings",
736
+ label: "Configuración",
737
+ path: "/settings",
738
+ icon: <SettingsIcon className="w-5 h-5" />,
739
+ },
740
+ ],
741
+ };
742
+
743
+ const handleLogout = async (user) => {
744
+ try {
745
+ await logoutUser(user);
746
+ navigate("/login");
747
+ } catch (error) {
748
+ console.error("Error al cerrar sesión:", error);
749
+ }
750
+ };
751
+
752
+ const handleTokenLogin = async (token) => {
753
+ try {
754
+ const newUser = await switchUserSession(token);
755
+ setUser(newUser);
756
+ navigate("/dashboard");
757
+ } catch (error) {
758
+ console.error("Error al cambiar sesión:", error);
759
+ }
760
+ };
761
+
762
+ const handleNotificationClick = () => {
763
+ setShowNotifications(true);
764
+ setNotificationCount(0);
765
+ };
766
+
767
+ return (
768
+ <div className="flex h-screen bg-gray-50">
769
+ <Sidebar
770
+ user={user}
771
+ sessions={sessions}
772
+ handleLogout={handleLogout}
773
+ handleTokenLogin={handleTokenLogin}
774
+ links={links}
775
+ isActiveModalNotification={showNotifications}
776
+ setIsActiveModalNotification={setShowNotifications}
777
+ handleNotificationClick={handleNotificationClick}
778
+ handleProfileClick={() => navigate("/profile")}
779
+ />
780
+ </div>
781
+ );
782
+ }
783
+
784
+ export default Navbar;
785
+
786
+
787
+ //<!--- En otro lugar de nuestra app>
788
+
789
+ /* REACT ROUTER */
790
+ <main className="flex-1 overflow-auto p-6">
791
+ <Routes>
792
+ <Route path="/dashboard" element={<DashboardContent />} />
793
+ <Route path="/tickets" element={<TicketsContent />} />
794
+ <Route path="/projects" element={<ProjectsContent />} />
795
+ <Route path="/reports" element={<ReportsContent />} />
796
+ <Route path="/notifications" element={<NotificationsContent />} />
797
+ <Route path="/settings" element={<SettingsContent />} />
798
+ <Route path="/profile" element={<ProfileContent />} />
799
+ </Routes>
800
+ </main>
801
+ ```