anima-ds-nucleus 1.0.2 → 1.0.4

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.
Files changed (38) hide show
  1. package/dist/anima-ds-nucleus.css +1 -1
  2. package/dist/anima-ds.cjs.js +152 -34
  3. package/dist/anima-ds.esm.js +10124 -7528
  4. package/package.json +10 -2
  5. package/src/assets/nucleus-logo.svg +3 -0
  6. package/src/components/Atoms/LogoHexa/LogoHexa.jsx +34 -0
  7. package/src/components/Atoms/LogoHexa/LogoHexa.stories.jsx +36 -0
  8. package/src/components/Atoms/Typography/Typography.jsx +53 -18
  9. package/src/components/Atoms/Typography/Typography.stories.jsx +40 -4
  10. package/src/components/DataDisplay/Card/Card.jsx +117 -24
  11. package/src/components/DataDisplay/Card/Card.stories.jsx +119 -35
  12. package/src/components/DataDisplay/Card/CardError.jsx +142 -0
  13. package/src/components/DataDisplay/Card/CardSkeleton.jsx +96 -0
  14. package/src/components/DataDisplay/Card/CardTituloCorto.jsx +61 -0
  15. package/src/components/DataDisplay/Card/CardTituloCortoMasEstado.jsx +79 -0
  16. package/src/components/DataDisplay/Card/CardTituloLargo.jsx +61 -0
  17. package/src/components/DataDisplay/Card/CardTituloLargoMasEstado.jsx +77 -0
  18. package/src/components/DataDisplay/Card/CardVacia.jsx +111 -0
  19. package/src/components/Layout/Header/HeaderConBuscador.jsx +136 -0
  20. package/src/components/Layout/Header/HeaderConBuscador.stories.jsx +86 -0
  21. package/src/components/Layout/Header/HeaderCore.jsx +347 -0
  22. package/src/components/Layout/Header/HeaderCore.stories.jsx +59 -0
  23. package/src/components/Layout/Header/HeaderGeneral.jsx +92 -0
  24. package/src/components/Layout/Header/HeaderGeneral.stories.jsx +64 -0
  25. package/src/components/Layout/Header/HeaderPoint.jsx +120 -0
  26. package/src/components/Layout/Header/HeaderPoint.stories.jsx +110 -0
  27. package/src/components/Layout/NavPoint/NavPoint.jsx +64 -0
  28. package/src/components/Layout/NavPoint/NavPoint.stories.jsx +52 -0
  29. package/src/components/Layout/Sidebar/SidebarCore.jsx +779 -0
  30. package/src/components/Layout/Sidebar/SidebarCore.stories.jsx +167 -0
  31. package/src/components/Layout/Sidebar/SidebarPoint.jsx +645 -0
  32. package/src/components/Layout/Sidebar/SidebarPoint.stories.jsx +183 -0
  33. package/src/index.js +15 -2
  34. package/src/style.css +37 -0
  35. package/src/components/Layout/Header/Header.jsx +0 -50
  36. package/src/components/Layout/Header/Header.stories.jsx +0 -36
  37. package/src/components/Layout/Sidebar/Sidebar.jsx +0 -57
  38. package/src/components/Layout/Sidebar/Sidebar.stories.jsx +0 -51
@@ -0,0 +1,779 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { Icon } from '../../Atoms/Icon/Icon';
3
+ import { Typography } from '../../Atoms/Typography/Typography';
4
+ import { LogoHexa } from '../../Atoms/LogoHexa/LogoHexa';
5
+
6
+ // Mock data con 2 títulos y 2 secciones
7
+ const MOCK_SECTIONS = [
8
+ {
9
+ title: 'PERSONAS',
10
+ items: [
11
+ { id: 'empleados', label: 'Empleados', icon: 'UserGroupIcon' },
12
+ { id: 'organizacion', label: 'Organización', icon: 'BuildingOfficeIcon' },
13
+ ],
14
+ },
15
+ {
16
+ title: 'TALENTO',
17
+ items: [
18
+ { id: 'reclutamiento', label: 'Reclutamiento', icon: 'UserPlusIcon' },
19
+ { id: 'desarrollo', label: 'Desarrollo', icon: 'AcademicCapIcon' },
20
+ ],
21
+ },
22
+ ];
23
+
24
+ // Componente SidebarCore Mobile
25
+ const SidebarCoreMobile = ({
26
+ sections = MOCK_SECTIONS,
27
+ activeItem,
28
+ onItemClick,
29
+ companyName = 'HEXA Core',
30
+ companyLogo,
31
+ onCompanyClick,
32
+ itemBadges = {},
33
+ footerText, // Texto del footer: puede ser string "v1.0 | Powered by Nucleus" o objeto { version: "v1.0", poweredBy: "Powered by", brand: "Nucleus" }
34
+ footerCollapsedContent, // Contenido a mostrar en el footer cuando está colapsado (puede ser string, ReactNode, o objeto con icon y text)
35
+ className = '',
36
+ ...props
37
+ }) => {
38
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
39
+
40
+ const toggleMobileMenu = () => {
41
+ setIsMobileMenuOpen(!isMobileMenuOpen);
42
+ };
43
+
44
+ // Generar footerCollapsedContent automáticamente si no se proporciona pero sí hay footerText
45
+ const getFooterCollapsedContent = () => {
46
+ if (footerCollapsedContent) {
47
+ return footerCollapsedContent;
48
+ }
49
+
50
+ // Si hay footerText pero no footerCollapsedContent, generar uno automáticamente
51
+ if (footerText && !footerCollapsedContent) {
52
+ if (typeof footerText === 'string') {
53
+ // Extraer la versión (parte antes del |) o usar todo el texto si no hay |
54
+ const parts = footerText.split('|');
55
+ return parts[0].trim() || footerText.trim();
56
+ } else if (typeof footerText === 'object' && footerText.version) {
57
+ return footerText.version;
58
+ }
59
+ }
60
+
61
+ return null;
62
+ };
63
+
64
+ const effectiveFooterCollapsedContent = getFooterCollapsedContent();
65
+
66
+ return (
67
+ <>
68
+ <style>{`
69
+ .sidebar-core-mobile {
70
+ width: 100%;
71
+ transform: ${isMobileMenuOpen ? 'translateX(0)' : 'translateX(-100%)'};
72
+ position: fixed;
73
+ left: 0;
74
+ top: 0;
75
+ height: 100vh;
76
+ z-index: 50;
77
+ transition: transform 0.3s ease-in-out;
78
+ }
79
+ `}</style>
80
+ {/* Botón hamburguesa - solo visible cuando el menú está cerrado */}
81
+ {!isMobileMenuOpen && (
82
+ <button
83
+ onClick={toggleMobileMenu}
84
+ className="fixed top-4 left-4 z-50 p-2 bg-white border border-gray-200 rounded-lg shadow-md
85
+ hover:bg-gray-50 transition-colors"
86
+ aria-label="Toggle menu"
87
+ >
88
+ <Icon
89
+ name="Bars3Icon"
90
+ variant="24-outline"
91
+ size={24}
92
+ className="color-gray-700"
93
+ />
94
+ </button>
95
+ )}
96
+ {/* Overlay para cerrar el menú */}
97
+ {isMobileMenuOpen && (
98
+ <div
99
+ className="fixed inset-0 bg-black bg-opacity-50 z-40"
100
+ onClick={toggleMobileMenu}
101
+ />
102
+ )}
103
+ <aside
104
+ className={`bg-white border-r border-gray-200 sidebar-core-mobile ${className}`}
105
+ {...props}
106
+ >
107
+ <nav className="h-full flex flex-col">
108
+ {/* Header con "Core" y botón X */}
109
+ <div className="bg-white px-4 py-4 flex items-center justify-between border-b border-gray-200">
110
+ <Typography
111
+ variant="h5"
112
+ style={{ color: '#223B40', fontSize: '24px', fontWeight: 'normal' }}
113
+ >
114
+ Core
115
+ </Typography>
116
+ <button
117
+ onClick={toggleMobileMenu}
118
+ className="p-1 hover:bg-gray-100 rounded transition-colors"
119
+ aria-label="Cerrar menú"
120
+ >
121
+ <Icon
122
+ name="XMarkIcon"
123
+ variant="24-outline"
124
+ size={24}
125
+ className="color-gray-700"
126
+ />
127
+ </button>
128
+ </div>
129
+ {/* Contenido del sidebar mobile */}
130
+ <div className="flex-1 overflow-y-auto py-4">
131
+ {/* Item "Inicio" destacado */}
132
+ <div className="px-4 mb-4">
133
+ <button
134
+ onClick={() => {
135
+ onItemClick && onItemClick('inicio');
136
+ toggleMobileMenu();
137
+ }}
138
+ className={`w-full flex items-center cursor-pointer px-4 justify-between py-2.5 rounded-lg transition-all duration-200 ${
139
+ activeItem === 'inicio'
140
+ ? ''
141
+ : 'color-gray-700 hover:bg-gray-100'
142
+ }`}
143
+ style={
144
+ activeItem === 'inicio'
145
+ ? { backgroundColor: '#2D5C63' }
146
+ : {}
147
+ }
148
+ >
149
+ <div className="flex items-center">
150
+ <Icon
151
+ name="HomeIcon"
152
+ variant="24-outline"
153
+ size={20}
154
+ className={`mr-3 ${
155
+ activeItem === 'inicio' ? 'color-white' : 'color-gray-700'
156
+ }`}
157
+ />
158
+ <Typography
159
+ variant="body-md"
160
+ className={`font-medium ${
161
+ activeItem === 'inicio' ? 'color-white' : 'color-gray-700'
162
+ }`}
163
+ >
164
+ Inicio
165
+ </Typography>
166
+ </div>
167
+ {itemBadges['inicio'] !== undefined && itemBadges['inicio'] > 0 && (
168
+ <span
169
+ className="px-2 py-0.5 min-w-[20px] h-5
170
+ text-white rounded-full flex items-center justify-center
171
+ text-body-sm font-medium"
172
+ style={{
173
+ backgroundColor: '#6D3856',
174
+ borderRadius: '12px'
175
+ }}
176
+ >
177
+ {itemBadges['inicio'] > 9 ? '9+' : itemBadges['inicio']}
178
+ </span>
179
+ )}
180
+ </button>
181
+ </div>
182
+
183
+ {/* Secciones */}
184
+ {sections.map((section, sectionIndex) => (
185
+ <div key={sectionIndex} className="mb-6">
186
+ {/* Título de la sección */}
187
+ <div className="px-4 mb-2">
188
+ <Typography
189
+ variant="body-sm"
190
+ className="color-gray-500 uppercase font-medium tracking-wider"
191
+ >
192
+ {section.title}
193
+ </Typography>
194
+ </div>
195
+
196
+ {/* Items de la sección */}
197
+ <div className="space-y-1">
198
+ {section.items.map((item) => (
199
+ <div key={item.id} className="px-4">
200
+ <button
201
+ onClick={() => {
202
+ onItemClick && onItemClick(item.id);
203
+ toggleMobileMenu();
204
+ }}
205
+ className={`w-full flex items-center cursor-pointer px-4 justify-between py-2.5 rounded-lg transition-all duration-200 ${
206
+ activeItem === item.id
207
+ ? ''
208
+ : 'color-gray-700 hover:bg-gray-100'
209
+ }`}
210
+ style={
211
+ activeItem === item.id
212
+ ? { backgroundColor: '#2D5C63' }
213
+ : {}
214
+ }
215
+ >
216
+ <div className="flex items-center">
217
+ <Icon
218
+ name={item.icon}
219
+ variant="24-outline"
220
+ size={20}
221
+ className={`mr-3 ${
222
+ activeItem === item.id ? 'color-white' : 'color-gray-700'
223
+ }`}
224
+ />
225
+ <Typography
226
+ variant="body-md"
227
+ className={`font-medium ${
228
+ activeItem === item.id ? 'color-white' : 'color-gray-700'
229
+ }`}
230
+ >
231
+ {item.label}
232
+ </Typography>
233
+ </div>
234
+ {((itemBadges[item.id] !== undefined && itemBadges[item.id] > 0) || (item.id === 'empleados' && 2)) && (
235
+ <span
236
+ className="px-2 py-0.5 min-w-[20px] h-5
237
+ text-white rounded-full flex items-center justify-center
238
+ text-body-sm font-medium"
239
+ style={{
240
+ backgroundColor: '#6D3856',
241
+ borderRadius: '12px'
242
+ }}
243
+ >
244
+ {item.id === 'empleados' ? 2 : (itemBadges[item.id] > 9 ? '9+' : itemBadges[item.id])}
245
+ </span>
246
+ )}
247
+ </button>
248
+ </div>
249
+ ))}
250
+ </div>
251
+ </div>
252
+ ))}
253
+ </div>
254
+
255
+ {/* Footer con texto configurable */}
256
+ {footerText && (
257
+ <div className="px-4 pb-4 flex-shrink-0">
258
+ <div
259
+ className="bg-white border rounded-lg px-3 py-2 flex items-center justify-center"
260
+ style={{
261
+ borderColor: '#2D5C63',
262
+ borderRadius: '8px'
263
+ }}
264
+ >
265
+ {typeof footerText === 'string' ? (
266
+ <Typography
267
+ variant="body-sm"
268
+ style={{ color: '#2D5C63' }}
269
+ className="text-center"
270
+ >
271
+ {(() => {
272
+ const parts = footerText.split('|');
273
+ if (parts.length === 2) {
274
+ const leftPart = parts[0].trim();
275
+ const rightPart = parts[1].trim();
276
+ // Si contiene "Powered by", separar y poner la marca en bold
277
+ if (rightPart.includes('Powered by')) {
278
+ const poweredByMatch = rightPart.match(/Powered by (.+)/);
279
+ if (poweredByMatch) {
280
+ return (
281
+ <>
282
+ {leftPart} | Powered by <span className="font-bold">{poweredByMatch[1]}</span>
283
+ </>
284
+ );
285
+ }
286
+ }
287
+ return `${leftPart} | ${rightPart}`;
288
+ }
289
+ return footerText;
290
+ })()}
291
+ </Typography>
292
+ ) : (
293
+ <Typography
294
+ variant="body-sm"
295
+ style={{ color: '#2D5C63' }}
296
+ className="text-center"
297
+ >
298
+ {footerText.version && `${footerText.version}`}
299
+ {footerText.version && (footerText.poweredBy || footerText.brand) && ' | '}
300
+ {footerText.poweredBy && `${footerText.poweredBy} `}
301
+ {footerText.brand && <span className="font-bold">{footerText.brand}</span>}
302
+ </Typography>
303
+ )}
304
+ </div>
305
+ </div>
306
+ )}
307
+ </nav>
308
+ </aside>
309
+ </>
310
+ );
311
+ };
312
+
313
+ export const SidebarCore = ({
314
+ sections = MOCK_SECTIONS,
315
+ activeItem,
316
+ onItemClick,
317
+ defaultCollapsed = false,
318
+ companyName = 'HEXA Core',
319
+ companyLogo,
320
+ onCompanyClick,
321
+ nucleusName = 'Nucleus AR',
322
+ nucleusLogo,
323
+ onNucleusClick,
324
+ itemBadges = {}, // Objeto con { itemId: number } para los badges
325
+ footerText, // Texto del footer: puede ser string "v1.0 | Powered by Nucleus" o objeto { version: "v1.0", poweredBy: "Powered by", brand: "Nucleus" }
326
+ footerCollapsedContent, // Contenido a mostrar en el footer cuando está colapsado (puede ser string, ReactNode, o objeto con icon y text)
327
+ className = '',
328
+ ...props
329
+ }) => {
330
+ const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
331
+ const [isMobile, setIsMobile] = useState(false);
332
+
333
+ // Detectar tamaño de pantalla
334
+ useEffect(() => {
335
+ const checkMobile = () => {
336
+ setIsMobile(window.innerWidth <= 480);
337
+ };
338
+
339
+ // Verificar al montar
340
+ checkMobile();
341
+
342
+ // Escuchar cambios de tamaño
343
+ window.addEventListener('resize', checkMobile);
344
+
345
+ return () => {
346
+ window.removeEventListener('resize', checkMobile);
347
+ };
348
+ }, []);
349
+
350
+ const toggleCollapse = () => {
351
+ setIsCollapsed(!isCollapsed);
352
+ };
353
+
354
+ // Generar footerCollapsedContent automáticamente si no se proporciona pero sí hay footerText
355
+ const getFooterCollapsedContent = () => {
356
+ if (footerCollapsedContent) {
357
+ return footerCollapsedContent;
358
+ }
359
+
360
+ // Si hay footerText pero no footerCollapsedContent, generar uno automáticamente
361
+ if (footerText && !footerCollapsedContent) {
362
+ if (typeof footerText === 'string') {
363
+ // Extraer la versión (parte antes del |) o usar todo el texto si no hay |
364
+ const parts = footerText.split('|');
365
+ return parts[0].trim() || footerText.trim();
366
+ } else if (typeof footerText === 'object' && footerText.version) {
367
+ return footerText.version;
368
+ }
369
+ }
370
+
371
+ return null;
372
+ };
373
+
374
+ const effectiveFooterCollapsedContent = getFooterCollapsedContent();
375
+
376
+ // Si es mobile, renderizar SidebarCoreMobile
377
+ if (isMobile) {
378
+ return (
379
+ <SidebarCoreMobile
380
+ sections={sections}
381
+ activeItem={activeItem}
382
+ onItemClick={onItemClick}
383
+ companyName={companyName}
384
+ companyLogo={companyLogo}
385
+ onCompanyClick={onCompanyClick}
386
+ itemBadges={itemBadges}
387
+ footerText={footerText}
388
+ footerCollapsedContent={footerCollapsedContent}
389
+ className={className}
390
+ {...props}
391
+ />
392
+ );
393
+ }
394
+
395
+ return (
396
+ <aside
397
+ className={`bg-white border-r border-gray-200 transition-all duration-300 ease-in-out h-full ${
398
+ isCollapsed ? 'w-20' : 'w-64'
399
+ } ${className}`}
400
+ {...props}
401
+ >
402
+ <nav className="h-full flex flex-col" style={{ overflow: 'hidden' }}>
403
+ {/* Barra superior con HEXA Core */}
404
+ {!isCollapsed && (
405
+ <div className="p-4 border-b border-gray-200 flex-shrink-0">
406
+ <button
407
+ onClick={onCompanyClick}
408
+ className="w-full bg-white rounded-lg px-3 py-2.5
409
+ flex items-center justify-between hover:bg-gray-50 transition-colors"
410
+ >
411
+ <div className="flex items-center space-x-3">
412
+ {/* Logo hexagonal */}
413
+ {companyLogo ? (
414
+ <img
415
+ src={companyLogo}
416
+ alt={companyName}
417
+ className="w-8 h-8 rounded"
418
+ />
419
+ ) : (
420
+ <LogoHexa width={36} height={40} />
421
+ )}
422
+ {/* Nombre de la empresa */}
423
+ <Typography
424
+ variant="body-md"
425
+ className="color-gray-900 font-medium"
426
+ >
427
+ {companyName}
428
+ </Typography>
429
+ </div>
430
+ </button>
431
+ </div>
432
+ )}
433
+
434
+ {/* Botón de colapsar/expandir - Solo visible cuando está expandido */}
435
+ {!isCollapsed && (
436
+ <div className="px-4 pt-2 pb-2 flex justify-end flex-shrink-0">
437
+ <button
438
+ onClick={toggleCollapse}
439
+ className="p-1.5 hover:bg-gray-100 rounded-lg transition-colors"
440
+ aria-label="Colapsar sidebar"
441
+ style={{ marginRight: '10px' }}
442
+ >
443
+ <Icon
444
+ name="ChevronDoubleLeftIcon"
445
+ variant="24-outline"
446
+ size={20}
447
+ className="color-teal"
448
+ />
449
+ </button>
450
+ </div>
451
+ )}
452
+
453
+ {/* Logo cuando está colapsado */}
454
+ {isCollapsed && (
455
+ <div className="p-4 border-b border-gray-200 flex justify-center flex-shrink-0">
456
+ {companyLogo ? (
457
+ <img
458
+ src={companyLogo}
459
+ alt={companyName}
460
+ className="w-8 h-8 rounded"
461
+ />
462
+ ) : (
463
+ <LogoHexa width={36} height={40} />
464
+ )}
465
+ </div>
466
+ )}
467
+
468
+ {/* Botón para expandir - Solo visible cuando está colapsado */}
469
+ {isCollapsed && (
470
+ <div className="px-4 pt-2 pb-2 flex justify-center flex-shrink-0">
471
+ <button
472
+ onClick={toggleCollapse}
473
+ className="p-1.5 hover:bg-gray-100 rounded-lg transition-colors"
474
+ aria-label="Expandir sidebar"
475
+ >
476
+ <Icon
477
+ name="ChevronDoubleRightIcon"
478
+ variant="24-outline"
479
+ size={20}
480
+ className="color-teal"
481
+ />
482
+ </button>
483
+ </div>
484
+ )}
485
+
486
+ {/* Contenido del sidebar - Wrapper para controlar el espacio */}
487
+ <div className="flex-1 min-h-0 flex flex-col overflow-hidden">
488
+ <div className={`flex-1 overflow-y-auto ${isCollapsed ? 'py-2' : 'py-4'}`} style={{ overflowX: 'hidden' }}>
489
+ {/* Item "Inicio" destacado */}
490
+ <div className="px-4 mb-4">
491
+ <button
492
+ onClick={() => onItemClick && onItemClick('inicio')}
493
+ className={`w-full flex items-center ${
494
+ isCollapsed ? 'justify-center px-2' : 'px-4 justify-between'
495
+ } py-2.5 rounded-lg transition-all duration-200 ${
496
+ activeItem === 'inicio'
497
+ ? ''
498
+ : 'color-gray-700 hover:bg-gray-100'
499
+ }`}
500
+ style={
501
+ activeItem === 'inicio'
502
+ ? { backgroundColor: '#2D5C63' }
503
+ : {}
504
+ }
505
+ >
506
+ <div className={`flex items-center ${isCollapsed ? 'relative' : ''}`} style={isCollapsed && itemBadges['inicio'] !== undefined && itemBadges['inicio'] > 0 ? { paddingRight: '16px' } : {}}>
507
+ <Icon
508
+ name="HomeIcon"
509
+ variant="24-outline"
510
+ size={20}
511
+ className={`${isCollapsed ? '' : 'mr-3'} ${
512
+ activeItem === 'inicio' ? 'color-white' : 'color-gray-700'
513
+ }`}
514
+ />
515
+ {!isCollapsed && (
516
+ <Typography
517
+ variant="body-md"
518
+ className={`font-medium ${
519
+ activeItem === 'inicio' ? 'color-white' : 'color-gray-700'
520
+ }`}
521
+ >
522
+ Inicio
523
+ </Typography>
524
+ )}
525
+ {isCollapsed && itemBadges['inicio'] !== undefined && itemBadges['inicio'] > 0 && (
526
+ <span
527
+ className="px-2 py-0.5 min-w-[20px] h-5
528
+ text-white rounded-full flex items-center justify-center
529
+ text-body-sm font-medium absolute"
530
+ style={{
531
+ backgroundColor: '#6D3856',
532
+ borderRadius: '12px',
533
+ top: '-4px',
534
+ right: '-4px',
535
+ zIndex: 10
536
+ }}
537
+ >
538
+ {itemBadges['inicio'] > 9 ? '9+' : itemBadges['inicio']}
539
+ </span>
540
+ )}
541
+ </div>
542
+ {!isCollapsed && itemBadges['inicio'] !== undefined && itemBadges['inicio'] > 0 && (
543
+ <span
544
+ className="px-2 py-0.5 min-w-[20px] h-5
545
+ text-white rounded-full flex items-center justify-center
546
+ text-body-sm font-medium"
547
+ style={{
548
+ backgroundColor: '#6D3856',
549
+ borderRadius: '12px'
550
+ }}
551
+ >
552
+ {itemBadges['inicio'] > 9 ? '9+' : itemBadges['inicio']}
553
+ </span>
554
+ )}
555
+ </button>
556
+ </div>
557
+
558
+ {/* Secciones */}
559
+ {sections.map((section, sectionIndex) => (
560
+ <div key={sectionIndex} className="mb-6">
561
+ {/* Línea separadora cuando está colapsado - antes de cada sección */}
562
+ {isCollapsed && (
563
+ <div className="px-4 mb-4">
564
+ <div className="border-t" style={{ borderColor: '#2D5C63' }}></div>
565
+ </div>
566
+ )}
567
+
568
+ {/* Título de la sección - solo visible cuando NO está colapsado */}
569
+ {!isCollapsed && (
570
+ <div className="px-4 mb-2">
571
+ <Typography
572
+ variant="body-sm"
573
+ className="color-gray-500 uppercase font-medium tracking-wider"
574
+ >
575
+ {section.title}
576
+ </Typography>
577
+ </div>
578
+ )}
579
+
580
+ {/* Items de la sección */}
581
+ <div className="space-y-1">
582
+ {section.items.map((item) => (
583
+ <div key={item.id} className="px-4">
584
+ <button
585
+ onClick={() => onItemClick && onItemClick(item.id)}
586
+ className={`w-full flex items-center ${
587
+ isCollapsed ? 'justify-center px-2' : 'px-4 justify-between'
588
+ } py-2.5 rounded-lg transition-all duration-200 ${
589
+ activeItem === item.id
590
+ ? ''
591
+ : 'color-gray-700 hover:bg-gray-100'
592
+ }`}
593
+ style={
594
+ activeItem === item.id
595
+ ? { backgroundColor: '#2D5C63' }
596
+ : {}
597
+ }
598
+ title={isCollapsed ? item.label : ''}
599
+ >
600
+ <div className={`flex items-center ${isCollapsed ? 'relative' : ''}`} style={isCollapsed && ((itemBadges[item.id] !== undefined && itemBadges[item.id] > 0) || (item.id === 'empleados' && 2)) ? { paddingRight: '16px' } : {}}>
601
+ <Icon
602
+ name={item.icon}
603
+ variant="24-outline"
604
+ size={20}
605
+ className={`${isCollapsed ? '' : 'mr-3'} ${
606
+ activeItem === item.id ? 'color-white' : 'color-gray-700'
607
+ }`}
608
+ />
609
+ {!isCollapsed && (
610
+ <Typography
611
+ variant="body-md"
612
+ className={`font-medium ${
613
+ activeItem === item.id ? 'color-white' : 'color-gray-700'
614
+ }`}
615
+ >
616
+ {item.label}
617
+ </Typography>
618
+ )}
619
+ {isCollapsed && ((itemBadges[item.id] !== undefined && itemBadges[item.id] > 0) || (item.id === 'empleados' && 2)) && (
620
+ <span
621
+ className="px-2 py-0.5 min-w-[20px] h-5
622
+ text-white rounded-full flex items-center justify-center
623
+ text-body-sm font-medium absolute"
624
+ style={{
625
+ backgroundColor: '#6D3856',
626
+ borderRadius: '12px',
627
+ top: '-4px',
628
+ right: '-4px',
629
+ zIndex: 10
630
+ }}
631
+ >
632
+ {item.id === 'empleados' ? 2 : (itemBadges[item.id] > 9 ? '9+' : itemBadges[item.id])}
633
+ </span>
634
+ )}
635
+ </div>
636
+ {!isCollapsed && ((itemBadges[item.id] !== undefined && itemBadges[item.id] > 0) || (item.id === 'empleados' && 2)) && (
637
+ <span
638
+ className="px-2 py-0.5 min-w-[20px] h-5
639
+ text-white rounded-full flex items-center justify-center
640
+ text-body-sm font-medium"
641
+ style={{
642
+ backgroundColor: '#6D3856',
643
+ borderRadius: '12px'
644
+ }}
645
+ >
646
+ {item.id === 'empleados' ? 2 : (itemBadges[item.id] > 9 ? '9+' : itemBadges[item.id])}
647
+ </span>
648
+ )}
649
+ </button>
650
+ </div>
651
+ ))}
652
+ </div>
653
+ </div>
654
+ ))}
655
+ </div>
656
+ </div>
657
+
658
+ {/* Footer con texto configurable - cuando está expandido */}
659
+ {footerText && !isCollapsed && (
660
+ <div className="px-4 pb-4 flex-shrink-0">
661
+ <div
662
+ className="bg-white border rounded-lg px-3 py-2 flex items-center justify-center"
663
+ style={{
664
+ borderColor: '#2D5C63',
665
+ borderRadius: '8px'
666
+ }}
667
+ >
668
+ {typeof footerText === 'string' ? (
669
+ <Typography
670
+ variant="body-sm"
671
+ style={{ color: '#2D5C63' }}
672
+ className="text-center"
673
+ >
674
+ {(() => {
675
+ const parts = footerText.split('|');
676
+ if (parts.length === 2) {
677
+ const leftPart = parts[0].trim();
678
+ const rightPart = parts[1].trim();
679
+ // Si contiene "Powered by", separar y poner la marca en bold
680
+ if (rightPart.includes('Powered by')) {
681
+ const poweredByMatch = rightPart.match(/Powered by (.+)/);
682
+ if (poweredByMatch) {
683
+ return (
684
+ <>
685
+ {leftPart} | Powered by <span className="font-bold">{poweredByMatch[1]}</span>
686
+ </>
687
+ );
688
+ }
689
+ }
690
+ return `${leftPart} | ${rightPart}`;
691
+ }
692
+ return footerText;
693
+ })()}
694
+ </Typography>
695
+ ) : (
696
+ <Typography
697
+ variant="body-sm"
698
+ style={{ color: '#2D5C63' }}
699
+ className="text-center"
700
+ >
701
+ {footerText.version && `${footerText.version}`}
702
+ {footerText.version && (footerText.poweredBy || footerText.brand) && ' | '}
703
+ {footerText.poweredBy && `${footerText.poweredBy} `}
704
+ {footerText.brand && <span className="font-bold">{footerText.brand}</span>}
705
+ </Typography>
706
+ )}
707
+ </div>
708
+ </div>
709
+ )}
710
+
711
+ {/* Footer cuando está colapsado */}
712
+ {effectiveFooterCollapsedContent && isCollapsed && (
713
+ <div className="flex justify-center flex-shrink-0" style={{ zIndex: 10, paddingTop: '10px', paddingBottom: '10px' }}>
714
+ <div
715
+ className="bg-white border flex items-center justify-center"
716
+ style={{
717
+ width: '52px',
718
+ height: '40px',
719
+ borderRadius: '8px',
720
+ border: '1px solid #29474C',
721
+ paddingTop: '8px',
722
+ paddingRight: '12px',
723
+ paddingBottom: '8px',
724
+ paddingLeft: '12px'
725
+ }}
726
+ >
727
+ {typeof effectiveFooterCollapsedContent === 'string' ? (
728
+ <span
729
+ style={{
730
+ color: '#2D5C63',
731
+ fontSize: '12px',
732
+ lineHeight: '1.2',
733
+ fontWeight: '600',
734
+ textAlign: 'center',
735
+ whiteSpace: 'nowrap',
736
+ overflow: 'hidden',
737
+ textOverflow: 'ellipsis',
738
+ maxWidth: '100%'
739
+ }}
740
+ >
741
+ {effectiveFooterCollapsedContent}
742
+ </span>
743
+ ) : typeof effectiveFooterCollapsedContent === 'object' && effectiveFooterCollapsedContent.icon ? (
744
+ <div className="flex items-center justify-center">
745
+ {effectiveFooterCollapsedContent.icon && (
746
+ <Icon
747
+ name={effectiveFooterCollapsedContent.icon}
748
+ variant="24-outline"
749
+ size={14}
750
+ style={{ color: '#2D5C63' }}
751
+ />
752
+ )}
753
+ {effectiveFooterCollapsedContent.text && (
754
+ <span
755
+ style={{
756
+ color: '#2D5C63',
757
+ fontSize: '12px',
758
+ marginLeft: effectiveFooterCollapsedContent.icon ? '4px' : '0'
759
+ }}
760
+ >
761
+ {effectiveFooterCollapsedContent.text}
762
+ </span>
763
+ )}
764
+ </div>
765
+ ) : (
766
+ effectiveFooterCollapsedContent
767
+ )}
768
+ </div>
769
+ </div>
770
+ )}
771
+ </nav>
772
+ </aside>
773
+ );
774
+ };
775
+
776
+ // Exportar ambos componentes
777
+ export { SidebarCoreMobile };
778
+ export default SidebarCore;
779
+