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.
- package/dist/anima-ds-nucleus.css +1 -1
- package/dist/anima-ds.cjs.js +152 -34
- package/dist/anima-ds.esm.js +10124 -7528
- package/package.json +10 -2
- package/src/assets/nucleus-logo.svg +3 -0
- package/src/components/Atoms/LogoHexa/LogoHexa.jsx +34 -0
- package/src/components/Atoms/LogoHexa/LogoHexa.stories.jsx +36 -0
- package/src/components/Atoms/Typography/Typography.jsx +53 -18
- package/src/components/Atoms/Typography/Typography.stories.jsx +40 -4
- package/src/components/DataDisplay/Card/Card.jsx +117 -24
- package/src/components/DataDisplay/Card/Card.stories.jsx +119 -35
- package/src/components/DataDisplay/Card/CardError.jsx +142 -0
- package/src/components/DataDisplay/Card/CardSkeleton.jsx +96 -0
- package/src/components/DataDisplay/Card/CardTituloCorto.jsx +61 -0
- package/src/components/DataDisplay/Card/CardTituloCortoMasEstado.jsx +79 -0
- package/src/components/DataDisplay/Card/CardTituloLargo.jsx +61 -0
- package/src/components/DataDisplay/Card/CardTituloLargoMasEstado.jsx +77 -0
- package/src/components/DataDisplay/Card/CardVacia.jsx +111 -0
- package/src/components/Layout/Header/HeaderConBuscador.jsx +136 -0
- package/src/components/Layout/Header/HeaderConBuscador.stories.jsx +86 -0
- package/src/components/Layout/Header/HeaderCore.jsx +347 -0
- package/src/components/Layout/Header/HeaderCore.stories.jsx +59 -0
- package/src/components/Layout/Header/HeaderGeneral.jsx +92 -0
- package/src/components/Layout/Header/HeaderGeneral.stories.jsx +64 -0
- package/src/components/Layout/Header/HeaderPoint.jsx +120 -0
- package/src/components/Layout/Header/HeaderPoint.stories.jsx +110 -0
- package/src/components/Layout/NavPoint/NavPoint.jsx +64 -0
- package/src/components/Layout/NavPoint/NavPoint.stories.jsx +52 -0
- package/src/components/Layout/Sidebar/SidebarCore.jsx +779 -0
- package/src/components/Layout/Sidebar/SidebarCore.stories.jsx +167 -0
- package/src/components/Layout/Sidebar/SidebarPoint.jsx +645 -0
- package/src/components/Layout/Sidebar/SidebarPoint.stories.jsx +183 -0
- package/src/index.js +15 -2
- package/src/style.css +37 -0
- package/src/components/Layout/Header/Header.jsx +0 -50
- package/src/components/Layout/Header/Header.stories.jsx +0 -36
- package/src/components/Layout/Sidebar/Sidebar.jsx +0 -57
- 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
|
+
|