anima-ds-nucleus 1.0.3 → 1.0.5
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 +124 -57
- package/dist/anima-ds.esm.js +6591 -5067
- 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 +4 -0
- package/src/components/Atoms/Typography/Typography.stories.jsx +37 -1
- 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 +62 -0
- package/src/components/DataDisplay/Card/CardTituloCortoMasEstado.jsx +80 -0
- package/src/components/DataDisplay/Card/CardTituloLargo.jsx +62 -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.stories.jsx +8 -0
- package/src/components/Layout/Header/HeaderCore.jsx +314 -85
- package/src/components/Layout/Header/HeaderCore.stories.jsx +25 -21
- 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 +524 -91
- package/src/components/Layout/Sidebar/SidebarCore.stories.jsx +76 -1
- package/src/components/Layout/Sidebar/SidebarPoint.jsx +264 -59
- package/src/components/Layout/Sidebar/SidebarPoint.stories.jsx +69 -0
- package/src/index.js +12 -3
- package/src/style.css +25 -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 -230
- package/src/components/Layout/Sidebar/Sidebar.stories.jsx +0 -90
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
2
|
import { Icon } from '../../Atoms/Icon/Icon';
|
|
3
3
|
import { Typography } from '../../Atoms/Typography/Typography';
|
|
4
|
+
import { LogoHexa } from '../../Atoms/LogoHexa/LogoHexa';
|
|
4
5
|
|
|
5
6
|
// Mock data con 2 títulos y 2 secciones
|
|
6
7
|
const MOCK_SECTIONS = [
|
|
@@ -20,6 +21,295 @@ const MOCK_SECTIONS = [
|
|
|
20
21
|
},
|
|
21
22
|
];
|
|
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
|
+
|
|
23
313
|
export const SidebarCore = ({
|
|
24
314
|
sections = MOCK_SECTIONS,
|
|
25
315
|
activeItem,
|
|
@@ -32,33 +322,94 @@ export const SidebarCore = ({
|
|
|
32
322
|
nucleusLogo,
|
|
33
323
|
onNucleusClick,
|
|
34
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)
|
|
35
327
|
className = '',
|
|
36
328
|
...props
|
|
37
329
|
}) => {
|
|
38
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 <= 768);
|
|
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
|
+
}, []);
|
|
39
349
|
|
|
40
350
|
const toggleCollapse = () => {
|
|
41
351
|
setIsCollapsed(!isCollapsed);
|
|
42
352
|
};
|
|
43
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
|
+
|
|
44
395
|
return (
|
|
45
396
|
<aside
|
|
46
|
-
className={`bg-white border-r border-gray-200 transition-all duration-300 ease-in-out ${
|
|
397
|
+
className={`bg-white border-r border-gray-200 transition-all duration-300 ease-in-out h-full ${
|
|
47
398
|
isCollapsed ? 'w-20' : 'w-64'
|
|
48
399
|
} ${className}`}
|
|
49
400
|
{...props}
|
|
50
401
|
>
|
|
51
|
-
<nav className="h-full flex flex-col overflow
|
|
402
|
+
<nav className="h-full flex flex-col" style={{ overflow: 'hidden' }}>
|
|
52
403
|
{/* Barra superior con HEXA Core */}
|
|
53
404
|
{!isCollapsed && (
|
|
54
|
-
<div className="p-4 border-b border-gray-200">
|
|
405
|
+
<div className="p-4 border-b border-gray-200 flex-shrink-0">
|
|
55
406
|
<button
|
|
56
407
|
onClick={onCompanyClick}
|
|
57
408
|
className="w-full bg-white rounded-lg px-3 py-2.5
|
|
58
409
|
flex items-center justify-between hover:bg-gray-50 transition-colors"
|
|
59
410
|
>
|
|
60
411
|
<div className="flex items-center space-x-3">
|
|
61
|
-
{/*
|
|
412
|
+
{/* Logo hexagonal */}
|
|
62
413
|
{companyLogo ? (
|
|
63
414
|
<img
|
|
64
415
|
src={companyLogo}
|
|
@@ -66,14 +417,7 @@ export const SidebarCore = ({
|
|
|
66
417
|
className="w-8 h-8 rounded"
|
|
67
418
|
/>
|
|
68
419
|
) : (
|
|
69
|
-
<
|
|
70
|
-
<Typography
|
|
71
|
-
variant="body-md"
|
|
72
|
-
className="color-white font-bold"
|
|
73
|
-
>
|
|
74
|
-
H
|
|
75
|
-
</Typography>
|
|
76
|
-
</div>
|
|
420
|
+
<LogoHexa width={36} height={40} />
|
|
77
421
|
)}
|
|
78
422
|
{/* Nombre de la empresa */}
|
|
79
423
|
<Typography
|
|
@@ -86,45 +430,10 @@ export const SidebarCore = ({
|
|
|
86
430
|
</button>
|
|
87
431
|
</div>
|
|
88
432
|
)}
|
|
89
|
-
{/* Botón Nucleus AR */}
|
|
90
|
-
{!isCollapsed && (
|
|
91
|
-
<div className="bg-gray-100 py-4 w-full">
|
|
92
|
-
<div className="px-4">
|
|
93
|
-
<button
|
|
94
|
-
onClick={onNucleusClick}
|
|
95
|
-
className="w-full flex items-center px-4 py-2.5 rounded-lg
|
|
96
|
-
transition-all duration-200 color-gray-700 hover:bg-gray-200
|
|
97
|
-
justify-between"
|
|
98
|
-
>
|
|
99
|
-
<div className="flex items-center">
|
|
100
|
-
<Icon
|
|
101
|
-
name="BuildingOfficeIcon"
|
|
102
|
-
variant="24-outline"
|
|
103
|
-
size={20}
|
|
104
|
-
className="mr-3 color-teal"
|
|
105
|
-
/>
|
|
106
|
-
<Typography
|
|
107
|
-
variant="body-md"
|
|
108
|
-
className="font-medium color-gray-700"
|
|
109
|
-
>
|
|
110
|
-
{nucleusName}
|
|
111
|
-
</Typography>
|
|
112
|
-
</div>
|
|
113
|
-
{/* Chevron hacia abajo */}
|
|
114
|
-
<Icon
|
|
115
|
-
name="ChevronDownIcon"
|
|
116
|
-
variant="24-outline"
|
|
117
|
-
size={16}
|
|
118
|
-
className="color-teal"
|
|
119
|
-
/>
|
|
120
|
-
</button>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
)}
|
|
124
433
|
|
|
125
434
|
{/* Botón de colapsar/expandir - Solo visible cuando está expandido */}
|
|
126
435
|
{!isCollapsed && (
|
|
127
|
-
<div className="px-4 pb-2 flex justify-end">
|
|
436
|
+
<div className="px-4 pt-2 pb-2 flex justify-end flex-shrink-0">
|
|
128
437
|
<button
|
|
129
438
|
onClick={toggleCollapse}
|
|
130
439
|
className="p-1.5 hover:bg-gray-100 rounded-lg transition-colors"
|
|
@@ -141,9 +450,9 @@ export const SidebarCore = ({
|
|
|
141
450
|
</div>
|
|
142
451
|
)}
|
|
143
452
|
|
|
144
|
-
{/* Logo
|
|
453
|
+
{/* Logo cuando está colapsado */}
|
|
145
454
|
{isCollapsed && (
|
|
146
|
-
<div className="p-4 border-b border-gray-200 flex justify-center">
|
|
455
|
+
<div className="p-4 border-b border-gray-200 flex justify-center flex-shrink-0">
|
|
147
456
|
{companyLogo ? (
|
|
148
457
|
<img
|
|
149
458
|
src={companyLogo}
|
|
@@ -151,47 +460,14 @@ export const SidebarCore = ({
|
|
|
151
460
|
className="w-8 h-8 rounded"
|
|
152
461
|
/>
|
|
153
462
|
) : (
|
|
154
|
-
<
|
|
155
|
-
<Typography
|
|
156
|
-
variant="body-md"
|
|
157
|
-
className="color-white font-bold"
|
|
158
|
-
>
|
|
159
|
-
H
|
|
160
|
-
</Typography>
|
|
161
|
-
</div>
|
|
463
|
+
<LogoHexa width={36} height={40} />
|
|
162
464
|
)}
|
|
163
465
|
</div>
|
|
164
466
|
)}
|
|
165
467
|
|
|
166
|
-
{/* Botón Nucleus AR cuando está colapsado */}
|
|
167
|
-
{isCollapsed && (
|
|
168
|
-
<div className="bg-gray-100 py-4 w-full">
|
|
169
|
-
<div className="px-4 flex justify-center">
|
|
170
|
-
<button
|
|
171
|
-
onClick={onNucleusClick}
|
|
172
|
-
className="flex items-center justify-center p-2.5 rounded-lg
|
|
173
|
-
transition-all duration-200 color-gray-700 hover:bg-gray-200"
|
|
174
|
-
>
|
|
175
|
-
<Icon
|
|
176
|
-
name="BuildingOfficeIcon"
|
|
177
|
-
variant="24-outline"
|
|
178
|
-
size={20}
|
|
179
|
-
className="color-teal"
|
|
180
|
-
/>
|
|
181
|
-
<Icon
|
|
182
|
-
name="ChevronDownIcon"
|
|
183
|
-
variant="24-outline"
|
|
184
|
-
size={16}
|
|
185
|
-
className="ml-1 color-teal"
|
|
186
|
-
/>
|
|
187
|
-
</button>
|
|
188
|
-
</div>
|
|
189
|
-
</div>
|
|
190
|
-
)}
|
|
191
|
-
|
|
192
468
|
{/* Botón para expandir - Solo visible cuando está colapsado */}
|
|
193
469
|
{isCollapsed && (
|
|
194
|
-
<div className="px-4 pb-2 flex justify-center">
|
|
470
|
+
<div className="px-4 pt-2 pb-2 flex justify-center flex-shrink-0">
|
|
195
471
|
<button
|
|
196
472
|
onClick={toggleCollapse}
|
|
197
473
|
className="p-1.5 hover:bg-gray-100 rounded-lg transition-colors"
|
|
@@ -207,8 +483,9 @@ export const SidebarCore = ({
|
|
|
207
483
|
</div>
|
|
208
484
|
)}
|
|
209
485
|
|
|
210
|
-
{/* Contenido del sidebar */}
|
|
211
|
-
<div className="flex-1
|
|
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' }}>
|
|
212
489
|
{/* Item "Inicio" destacado */}
|
|
213
490
|
<div className="px-4 mb-4">
|
|
214
491
|
<button
|
|
@@ -226,7 +503,7 @@ export const SidebarCore = ({
|
|
|
226
503
|
: {}
|
|
227
504
|
}
|
|
228
505
|
>
|
|
229
|
-
<div className=
|
|
506
|
+
<div className={`flex items-center ${isCollapsed ? 'relative' : ''}`} style={isCollapsed && itemBadges['inicio'] !== undefined && itemBadges['inicio'] > 0 ? { paddingRight: '16px' } : {}}>
|
|
230
507
|
<Icon
|
|
231
508
|
name="HomeIcon"
|
|
232
509
|
variant="24-outline"
|
|
@@ -245,6 +522,22 @@ export const SidebarCore = ({
|
|
|
245
522
|
Inicio
|
|
246
523
|
</Typography>
|
|
247
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
|
+
)}
|
|
248
541
|
</div>
|
|
249
542
|
{!isCollapsed && itemBadges['inicio'] !== undefined && itemBadges['inicio'] > 0 && (
|
|
250
543
|
<span
|
|
@@ -265,7 +558,14 @@ export const SidebarCore = ({
|
|
|
265
558
|
{/* Secciones */}
|
|
266
559
|
{sections.map((section, sectionIndex) => (
|
|
267
560
|
<div key={sectionIndex} className="mb-6">
|
|
268
|
-
{/*
|
|
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 */}
|
|
269
569
|
{!isCollapsed && (
|
|
270
570
|
<div className="px-4 mb-2">
|
|
271
571
|
<Typography
|
|
@@ -297,7 +597,7 @@ export const SidebarCore = ({
|
|
|
297
597
|
}
|
|
298
598
|
title={isCollapsed ? item.label : ''}
|
|
299
599
|
>
|
|
300
|
-
<div className=
|
|
600
|
+
<div className={`flex items-center ${isCollapsed ? 'relative' : ''}`} style={isCollapsed && ((itemBadges[item.id] !== undefined && itemBadges[item.id] > 0) || (item.id === 'empleados' && 2)) ? { paddingRight: '16px' } : {}}>
|
|
301
601
|
<Icon
|
|
302
602
|
name={item.icon}
|
|
303
603
|
variant="24-outline"
|
|
@@ -316,6 +616,22 @@ export const SidebarCore = ({
|
|
|
316
616
|
{item.label}
|
|
317
617
|
</Typography>
|
|
318
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
|
+
)}
|
|
319
635
|
</div>
|
|
320
636
|
{!isCollapsed && ((itemBadges[item.id] !== undefined && itemBadges[item.id] > 0) || (item.id === 'empleados' && 2)) && (
|
|
321
637
|
<span
|
|
@@ -336,11 +652,128 @@ export const SidebarCore = ({
|
|
|
336
652
|
</div>
|
|
337
653
|
</div>
|
|
338
654
|
))}
|
|
655
|
+
</div>
|
|
339
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
|
+
)}
|
|
340
771
|
</nav>
|
|
341
772
|
</aside>
|
|
342
773
|
);
|
|
343
774
|
};
|
|
344
775
|
|
|
776
|
+
// Exportar ambos componentes
|
|
777
|
+
export { SidebarCoreMobile };
|
|
345
778
|
export default SidebarCore;
|
|
346
779
|
|