siesa-ui-kit 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/README.md +115 -115
- package/bin/install.cjs +502 -502
- package/bin/prepare-publish.cjs +28 -28
- package/bin/restore-folders.cjs +28 -28
- package/claude/agents/siesa-ui-kit-specialist.md +2445 -0
- package/claude/prompts/component-template.md +121 -0
- package/claude/prompts/siesa-ui-kit.md +28 -0
- package/claude/settings.local.json +67 -2
- package/dist/components/Button/icons.d.ts +6 -5
- package/dist/components/Button/icons.d.ts.map +1 -1
- package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.d.ts.map +1 -1
- package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.types.d.ts +21 -0
- package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.types.d.ts.map +1 -1
- package/dist/components/NavigationRailCommercial/NavigationRailCommercial.d.ts +122 -0
- package/dist/components/NavigationRailCommercial/NavigationRailCommercial.d.ts.map +1 -0
- package/dist/components/NavigationRailCommercial/NavigationRailCommercial.types.d.ts +139 -0
- package/dist/components/NavigationRailCommercial/NavigationRailCommercial.types.d.ts.map +1 -0
- package/dist/components/NavigationRailCommercial/icons.d.ts +33 -0
- package/dist/components/NavigationRailCommercial/icons.d.ts.map +1 -0
- package/dist/components/NavigationRailCommercial/index.d.ts +4 -0
- package/dist/components/NavigationRailCommercial/index.d.ts.map +1 -0
- package/dist/components/NavigationRailItem/NavigationRailItem.d.ts.map +1 -1
- package/dist/components/NavigationRailItem/NavigationRailItem.types.d.ts +7 -0
- package/dist/components/NavigationRailItem/NavigationRailItem.types.d.ts.map +1 -1
- package/dist/components/NavigationRailTypes/NavigationRailTypes.d.ts.map +1 -1
- package/dist/components/NavigationRailTypes/NavigationRailTypes.types.d.ts +41 -0
- package/dist/components/NavigationRailTypes/NavigationRailTypes.types.d.ts.map +1 -1
- package/dist/components/NavigationRailTypes/icons.d.ts +15 -29
- package/dist/components/NavigationRailTypes/icons.d.ts.map +1 -1
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/icons.d.ts +6 -2
- package/dist/components/Select/icons.d.ts.map +1 -1
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/siesa-ui-kit.cjs +404 -190
- package/dist/siesa-ui-kit.cjs.map +1 -1
- package/dist/siesa-ui-kit.mjs +6590 -1506
- package/dist/siesa-ui-kit.mjs.map +1 -1
- package/dist/views/LayoutCommercial/LayoutCommercial.d.ts +48 -0
- package/dist/views/LayoutCommercial/LayoutCommercial.d.ts.map +1 -0
- package/dist/views/LayoutCommercial/LayoutCommercial.types.d.ts +49 -0
- package/dist/views/LayoutCommercial/LayoutCommercial.types.d.ts.map +1 -0
- package/dist/views/LayoutCommercial/index.d.ts +3 -0
- package/dist/views/LayoutCommercial/index.d.ts.map +1 -0
- package/docs/icons.md +12 -31
- package/package.json +111 -110
- package/src/components/Avatar/Avatar.stories.tsx +494 -494
- package/src/components/Button/Button.stories.tsx +950 -950
- package/src/components/Button/Button.tsx +337 -337
- package/src/components/Button/Button.types.ts +180 -180
- package/src/components/Button/icons.tsx +23 -62
- package/src/components/DescriptionList/DescriptionList.stories.tsx +250 -250
- package/src/components/Divider/Divider.stories.tsx +263 -263
- package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.stories.tsx +317 -317
- package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.tsx +307 -287
- package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.types.ts +136 -111
- package/src/components/DropdownItemCollapsible/README.md +264 -264
- package/src/components/DropdownItemCollapsible/icons.tsx +57 -57
- package/src/components/DropdownItemCollapsible/index.ts +12 -12
- package/src/components/DropdownItemHeading/DropdownItemHeading.stories.tsx +386 -386
- package/src/components/DropdownItemHeading/DropdownItemHeading.tsx +216 -216
- package/src/components/DropdownItemHeading/DropdownItemHeading.types.ts +93 -93
- package/src/components/DropdownItemHeading/README.md +573 -573
- package/src/components/DropdownItemHeading/icons.tsx +125 -125
- package/src/components/DropdownItemHeading/index.ts +3 -3
- package/src/components/Input/Input.stories.tsx +583 -583
- package/src/components/LoginView/LoginView.stories.tsx +148 -148
- package/src/components/LoginView/LoginView.tsx +426 -426
- package/src/components/LoginView/LoginView.types.ts +52 -52
- package/src/components/LoginView/README.md +396 -396
- package/src/components/LoginView/icons.tsx +85 -85
- package/src/components/LoginView/index.ts +3 -3
- package/src/components/Navbar/Navbar.stories.tsx +810 -810
- package/src/components/Navbar/Navbar.tsx +755 -755
- package/src/components/Navbar/Navbar.types.ts +219 -219
- package/src/components/Navbar/README.md +279 -279
- package/src/components/Navbar/index.ts +8 -8
- package/src/components/NavigationRailCommercial/NavigationRailCommercial.stories.tsx +464 -0
- package/src/components/NavigationRailCommercial/NavigationRailCommercial.tsx +301 -0
- package/src/components/NavigationRailCommercial/NavigationRailCommercial.types.ts +162 -0
- package/src/components/NavigationRailCommercial/README.md +251 -0
- package/src/components/NavigationRailCommercial/icons.tsx +54 -0
- package/src/components/NavigationRailCommercial/index.ts +6 -0
- package/src/components/NavigationRailItem/NavigationRailItem.stories.tsx +667 -667
- package/src/components/NavigationRailItem/NavigationRailItem.tsx +314 -313
- package/src/components/NavigationRailItem/NavigationRailItem.types.ts +175 -167
- package/src/components/NavigationRailItem/README.md +476 -476
- package/src/components/NavigationRailItem/index.ts +2 -2
- package/src/components/NavigationRailPanel/NavigationRailPanel.stories.tsx +462 -462
- package/src/components/NavigationRailPanel/NavigationRailPanel.tsx +332 -332
- package/src/components/NavigationRailPanel/NavigationRailPanel.types.ts +178 -178
- package/src/components/NavigationRailPanel/README.md +461 -461
- package/src/components/NavigationRailPanel/index.ts +6 -6
- package/src/components/NavigationRailTypes/NavigationRailTypes.stories.tsx +682 -528
- package/src/components/NavigationRailTypes/NavigationRailTypes.tsx +363 -378
- package/src/components/NavigationRailTypes/NavigationRailTypes.types.ts +178 -130
- package/src/components/NavigationRailTypes/README.md +573 -573
- package/src/components/NavigationRailTypes/icons.tsx +76 -141
- package/src/components/NavigationRailTypes/index.ts +7 -7
- package/src/components/Notification/Notification.stories.tsx +513 -513
- package/src/components/Notification/Notification.tsx +145 -145
- package/src/components/Notification/Notification.types.ts +142 -142
- package/src/components/Notification/README.md +409 -409
- package/src/components/POSConvention/POSConvention.stories.tsx +235 -235
- package/src/components/POSConvention/POSConvention.tsx +129 -129
- package/src/components/POSConvention/POSConvention.types.ts +38 -38
- package/src/components/POSConvention/README.md +123 -123
- package/src/components/POSConvention/icons.tsx +45 -45
- package/src/components/POSConvention/index.ts +3 -3
- package/src/components/POSLocationButton/POSLocationButton.stories.tsx +531 -531
- package/src/components/POSLocationButton/POSLocationButton.tsx +247 -247
- package/src/components/POSLocationButton/POSLocationButton.types.ts +87 -87
- package/src/components/POSLocationButton/README.md +253 -253
- package/src/components/POSLocationButton/icons.tsx +120 -120
- package/src/components/POSLocationButton/index.ts +14 -14
- package/src/components/POSNumberButton/POSNumberButton.stories.tsx +415 -415
- package/src/components/POSNumberButton/POSNumberButton.tsx +179 -179
- package/src/components/POSNumberButton/POSNumberButton.types.ts +51 -51
- package/src/components/POSNumberButton/README.md +321 -321
- package/src/components/POSNumberButton/index.ts +3 -3
- package/src/components/POSProductButton/POSProductButton.stories.tsx +318 -318
- package/src/components/POSProductCard/POSProductCard.stories.tsx +642 -642
- package/src/components/POSProductCard/POSProductCard.tsx +208 -208
- package/src/components/POSProductCard/POSProductCard.types.ts +76 -76
- package/src/components/POSProductCard/README.md +179 -179
- package/src/components/POSProductCard/icons.tsx +26 -26
- package/src/components/POSProductCard/index.ts +2 -2
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.stories.tsx +753 -753
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.tsx +332 -332
- package/src/components/POSProductSidebarItems/POSProductSidebarItems.types.ts +119 -119
- package/src/components/POSProductSidebarItems/README.md +198 -198
- package/src/components/POSProductSidebarItems/icons.tsx +21 -21
- package/src/components/POSProductSidebarItems/index.ts +3 -3
- package/src/components/POSTable/POSTable.stories.tsx +737 -737
- package/src/components/POSTable/POSTable.tsx +401 -401
- package/src/components/POSTable/README.md +286 -286
- package/src/components/Quantity/Quantity.stories.tsx +457 -457
- package/src/components/Radio/Radio.stories.tsx +523 -523
- package/src/components/Radio/Radio.tsx +1 -1
- package/src/components/Select/Select.stories.tsx +32 -0
- package/src/components/Select/Select.tsx +457 -454
- package/src/components/Select/icons.tsx +16 -41
- package/src/components/SignUpView/SignUpView.stories.tsx +129 -129
- package/src/components/SignUpView/SignUpView.tsx +503 -503
- package/src/components/SignUpView/SignUpView.types.ts +58 -58
- package/src/components/SignUpView/icons.tsx +71 -71
- package/src/components/SignUpView/index.ts +3 -3
- package/src/components/Switch/README.md +112 -112
- package/src/components/Switch/Switch.stories.tsx +550 -550
- package/src/components/Switch/Switch.tsx +246 -246
- package/src/components/Switch/Switch.types.ts +67 -67
- package/src/components/Table/Table.stories.tsx +805 -805
- package/src/components/Tabs/README.md +201 -201
- package/src/components/Tabs/Tabs.stories.tsx +580 -580
- package/src/components/Tabs/Tabs.tsx +356 -356
- package/src/components/Tabs/Tabs.types.ts +127 -127
- package/src/components/Tabs/icons.tsx +129 -129
- package/src/components/Tabs/index.ts +11 -11
- package/src/components/Textarea/Textarea.stories.tsx +535 -535
- package/src/index.ts +133 -102
- package/src/views/LayoutCommercial/LayoutCommercial.stories.tsx +374 -0
- package/src/views/LayoutCommercial/LayoutCommercial.tsx +125 -0
- package/src/views/LayoutCommercial/LayoutCommercial.types.ts +54 -0
- package/src/views/LayoutCommercial/README.md +286 -0
- package/src/views/LayoutCommercial/index.ts +2 -0
- package/src/views/ListView/ListView.stories.tsx +329 -329
- package/src/views/ListView/ListView.tsx +570 -570
- package/src/views/ListView/ListView.types.ts +211 -211
- package/src/views/ListView/icons.tsx +282 -282
- package/src/views/ListView/index.ts +11 -11
- package/src/views/LoginView/LoginView.tsx +426 -426
- package/src/views/ProductsView/ProductsView.stories.tsx +344 -344
- package/src/views/ProductsView/ProductsView.tsx +480 -480
- package/src/views/ProductsView/ProductsView.types.ts +238 -238
- package/src/views/ProductsView/README.md +312 -312
- package/src/views/ProductsView/icons.tsx +38 -38
- package/src/views/ProductsView/index.ts +8 -8
- package/src/views/RecoverPasswordView/RecoverPasswordView.tsx +376 -376
- package/src/views/SignUpView/SignUpView.tsx +503 -503
- package/src/views/TableLayoutView/README.md +268 -268
- package/src/views/TableLayoutView/TableLayoutView.stories.tsx +235 -235
- package/src/views/TableLayoutView/TableLayoutView.tsx +461 -461
- package/src/views/TableLayoutView/TableLayoutView.types.ts +209 -209
- package/src/views/TableLayoutView/icons.tsx +113 -113
- package/src/views/TableLayoutView/index.ts +6 -6
- package/storybook/main.ts +19 -19
- package/storybook/preview.tsx +84 -84
- package/storybook/vitest.setup.ts +6 -6
- package/tailwind.config.js +128 -128
|
@@ -1,247 +1,247 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import type { POSLocationButtonProps } from './POSLocationButton.types';
|
|
3
|
-
import { CheckIcon, XMarkIcon, CalendarIcon, NoSymbolIcon, UsersIcon } from './icons';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* POSLocationButton del sistema de diseño Siesa
|
|
7
|
-
*
|
|
8
|
-
* Botón especializado para punto de venta (POS) que muestra el estado de disponibilidad
|
|
9
|
-
* de una ubicación física (mesa, salón, terraza, etc.) con indicadores visuales claros.
|
|
10
|
-
*
|
|
11
|
-
* **Estados de disponibilidad (status):**
|
|
12
|
-
* - `available`: Disponible (verde/lime) con icono ✓
|
|
13
|
-
* - `occupied`: Ocupada (naranja/yellow) con icono ✗
|
|
14
|
-
* - `reserved`: Reservada (morado/purple) con icono 📅
|
|
15
|
-
* - `outOfService`: Fuera de servicio (gris/zinc) con icono 🚫
|
|
16
|
-
*
|
|
17
|
-
* **Estados visuales (state):**
|
|
18
|
-
* - `enabled`: Estado normal con fondo blanco/dark
|
|
19
|
-
* - `actived`: Estado seleccionado con fondo coloreado
|
|
20
|
-
*
|
|
21
|
-
* **Características:**
|
|
22
|
-
* - Ancho flexible: por defecto ocupa el 100% del contenedor (fullWidth=true)
|
|
23
|
-
* - Altura fija: 68px (optimizado para interfaces POS)
|
|
24
|
-
* - Muestra nombre de ubicación y capacidad (ej: "1/8 mesas")
|
|
25
|
-
* - Badge visual con icono y texto del estado
|
|
26
|
-
* - Dark mode completo en todos los estados
|
|
27
|
-
* - Focus ring adaptativo
|
|
28
|
-
* - Transiciones suaves
|
|
29
|
-
*
|
|
30
|
-
* Mejores prácticas implementadas:
|
|
31
|
-
* - Orden de modificadores: {responsive}:{dark}:{state}:{utility}
|
|
32
|
-
* - Dark mode con estrategia 'class' (darkMode: 'class')
|
|
33
|
-
* - Tokens de color consistentes con la documentación
|
|
34
|
-
* - Type safety con TypeScript estricto
|
|
35
|
-
* - Accesibilidad con ARIA labels
|
|
36
|
-
*
|
|
37
|
-
* @see docs/colors.md - Sistema de colores
|
|
38
|
-
* @see docs/typography.md - Sistema tipográfico
|
|
39
|
-
* @see docs/spacing.md - Sistema de espaciado
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```tsx
|
|
43
|
-
* // Ubicación disponible
|
|
44
|
-
* <POSLocationButton
|
|
45
|
-
* locationName="Antejardín"
|
|
46
|
-
* status="available"
|
|
47
|
-
* capacity={{ current: 1, total: 8 }}
|
|
48
|
-
* onClick={() => console.log('Seleccionado')}
|
|
49
|
-
* />
|
|
50
|
-
*
|
|
51
|
-
* // Ubicación ocupada y seleccionada
|
|
52
|
-
* <POSLocationButton
|
|
53
|
-
* locationName="Terraza"
|
|
54
|
-
* status="occupied"
|
|
55
|
-
* state="actived"
|
|
56
|
-
* capacity={{ current: 5, total: 8 }}
|
|
57
|
-
* />
|
|
58
|
-
*
|
|
59
|
-
* // Ubicación reservada
|
|
60
|
-
* <POSLocationButton
|
|
61
|
-
* locationName="Salón Principal"
|
|
62
|
-
* status="reserved"
|
|
63
|
-
* capacity={{ current: 3, total: 12 }}
|
|
64
|
-
* />
|
|
65
|
-
*
|
|
66
|
-
* // Fuera de servicio
|
|
67
|
-
* <POSLocationButton
|
|
68
|
-
* locationName="Bar"
|
|
69
|
-
* status="outOfService"
|
|
70
|
-
* disabled
|
|
71
|
-
* />
|
|
72
|
-
* ```
|
|
73
|
-
*/
|
|
74
|
-
export const POSLocationButton: React.FC<POSLocationButtonProps> = ({
|
|
75
|
-
locationName,
|
|
76
|
-
status = 'available',
|
|
77
|
-
state = 'enabled',
|
|
78
|
-
capacity,
|
|
79
|
-
onClick,
|
|
80
|
-
disabled = false,
|
|
81
|
-
className = '',
|
|
82
|
-
ariaLabel,
|
|
83
|
-
fullWidth = true,
|
|
84
|
-
}) => {
|
|
85
|
-
// ===== CONFIGURACIÓN DE COLORES POR STATUS =====
|
|
86
|
-
// Mapeo de colores según especificaciones de Figma (pixel-perfect)
|
|
87
|
-
const statusConfig = {
|
|
88
|
-
available: {
|
|
89
|
-
// Azul Primary Custom (según Figma: #0e79fd)
|
|
90
|
-
textColor: 'text-primary-custom-600 dark:text-primary-custom-600',
|
|
91
|
-
badgeBg: 'bg-primary-custom-100 dark:bg-blue-900/30',
|
|
92
|
-
badgeText: 'text-primary-custom-600 dark:text-blue-400',
|
|
93
|
-
activedBg: 'bg-primary-custom-100 dark:bg-blue-900/30',
|
|
94
|
-
icon: CheckIcon,
|
|
95
|
-
badgeLabel: 'Disponible',
|
|
96
|
-
},
|
|
97
|
-
occupied: {
|
|
98
|
-
// Naranja/Yellow (según Figma: #af460e para texto, #fcedc9 para fondo)
|
|
99
|
-
textColor: 'text-[#af460e] dark:text-orange-400',
|
|
100
|
-
badgeBg: 'bg-[#fcedc9] dark:bg-yellow-900/30',
|
|
101
|
-
badgeText: 'text-[#af460e] dark:text-orange-400',
|
|
102
|
-
activedBg: 'bg-[#fcedc9] dark:bg-yellow-900/30',
|
|
103
|
-
icon: XMarkIcon,
|
|
104
|
-
badgeLabel: 'Ocupada',
|
|
105
|
-
},
|
|
106
|
-
reserved: {
|
|
107
|
-
// Morado/Purple (según Figma: #7e22ce para texto, #f3e8ff para fondo)
|
|
108
|
-
textColor: 'text-[#7e22ce] dark:text-fuchsia-400',
|
|
109
|
-
badgeBg: 'bg-[#f3e8ff] dark:bg-purple-900/30',
|
|
110
|
-
badgeText: 'text-[#7e22ce] dark:text-purple-400',
|
|
111
|
-
activedBg: 'bg-[#f3e8ff] dark:bg-purple-900/30',
|
|
112
|
-
icon: CalendarIcon,
|
|
113
|
-
badgeLabel: 'Reservada',
|
|
114
|
-
},
|
|
115
|
-
outOfService: {
|
|
116
|
-
// Gris/Zinc (según Figma: #3f3f46 para texto, #f4f4f5 para fondo)
|
|
117
|
-
textColor: 'text-[#3f3f46] dark:text-zinc-400',
|
|
118
|
-
badgeBg: 'bg-[#f4f4f5] dark:bg-zinc-800/30',
|
|
119
|
-
badgeText: 'text-content-tertiary dark:text-zinc-400',
|
|
120
|
-
activedBg: 'bg-[#f4f4f5] dark:bg-zinc-800/30',
|
|
121
|
-
icon: NoSymbolIcon,
|
|
122
|
-
badgeLabel: 'F. de Servicio',
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const config = statusConfig[status];
|
|
127
|
-
const StatusIcon = config.icon;
|
|
128
|
-
|
|
129
|
-
// ===== CLASES DE FONDO SEGÚN STATE =====
|
|
130
|
-
const backgroundClasses =
|
|
131
|
-
state === 'actived'
|
|
132
|
-
? config.activedBg // Fondo coloreado cuando está activo
|
|
133
|
-
: 'bg-white dark:bg-dark-bg-primary'; // Fondo blanco/dark cuando está enabled
|
|
134
|
-
|
|
135
|
-
// ===== CLASES DE ANCHO =====
|
|
136
|
-
const widthClasses = fullWidth ? 'w-full' : 'w-[189px]';
|
|
137
|
-
|
|
138
|
-
// ===== CLASES BASE =====
|
|
139
|
-
const baseClasses = `
|
|
140
|
-
flex
|
|
141
|
-
flex-col
|
|
142
|
-
items-stretch
|
|
143
|
-
justify-between
|
|
144
|
-
${widthClasses}
|
|
145
|
-
h-[68px]
|
|
146
|
-
p-2
|
|
147
|
-
rounded-lg
|
|
148
|
-
cursor-pointer
|
|
149
|
-
transition-all
|
|
150
|
-
duration-150
|
|
151
|
-
focus:outline-none
|
|
152
|
-
focus:ring-2
|
|
153
|
-
focus:ring-primary-custom-400
|
|
154
|
-
focus:ring-offset-2
|
|
155
|
-
dark:focus:ring-dark-border-custom
|
|
156
|
-
dark:focus:ring-offset-dark-bg-primary
|
|
157
|
-
hover:shadow-md
|
|
158
|
-
active:scale-[0.98]
|
|
159
|
-
disabled:opacity-50
|
|
160
|
-
disabled:cursor-not-allowed
|
|
161
|
-
disabled:pointer-events-none
|
|
162
|
-
`;
|
|
163
|
-
|
|
164
|
-
// ===== COMBINAR CLASES =====
|
|
165
|
-
const buttonClasses = [baseClasses, backgroundClasses, className]
|
|
166
|
-
.join(' ')
|
|
167
|
-
.replace(/\s+/g, ' ')
|
|
168
|
-
.trim();
|
|
169
|
-
|
|
170
|
-
// ===== FORMATEAR TEXTO DE CAPACIDAD =====
|
|
171
|
-
const capacityText = capacity
|
|
172
|
-
? `${capacity.current}/${capacity.total} mesas`
|
|
173
|
-
: null;
|
|
174
|
-
|
|
175
|
-
return (
|
|
176
|
-
<button
|
|
177
|
-
className={buttonClasses}
|
|
178
|
-
onClick={onClick}
|
|
179
|
-
disabled={disabled}
|
|
180
|
-
aria-label={ariaLabel || `${locationName} - ${config.badgeLabel}`}
|
|
181
|
-
type="button"
|
|
182
|
-
>
|
|
183
|
-
{/* ===== TÍTULO (Nombre de ubicación) ===== */}
|
|
184
|
-
<div
|
|
185
|
-
className={`
|
|
186
|
-
text-base
|
|
187
|
-
font-bold
|
|
188
|
-
leading-6
|
|
189
|
-
${config.textColor}
|
|
190
|
-
`
|
|
191
|
-
.replace(/\s+/g, ' ')
|
|
192
|
-
.trim()}
|
|
193
|
-
>
|
|
194
|
-
{locationName}
|
|
195
|
-
</div>
|
|
196
|
-
|
|
197
|
-
{/* ===== CONTENIDO (Capacidad + Badge) ===== */}
|
|
198
|
-
<div className="flex items-center justify-between">
|
|
199
|
-
{/* Capacidad (izquierda) */}
|
|
200
|
-
{capacityText && (
|
|
201
|
-
<div className="flex items-center gap-1">
|
|
202
|
-
<UsersIcon className={config.textColor} />
|
|
203
|
-
<span
|
|
204
|
-
className={`
|
|
205
|
-
text-[10px]
|
|
206
|
-
leading-[12px]
|
|
207
|
-
${config.textColor}
|
|
208
|
-
`
|
|
209
|
-
.replace(/\s+/g, ' ')
|
|
210
|
-
.trim()}
|
|
211
|
-
>
|
|
212
|
-
{capacityText}
|
|
213
|
-
</span>
|
|
214
|
-
</div>
|
|
215
|
-
)}
|
|
216
|
-
|
|
217
|
-
{/* Badge (derecha) */}
|
|
218
|
-
<div
|
|
219
|
-
className={`
|
|
220
|
-
inline-flex
|
|
221
|
-
items-center
|
|
222
|
-
gap-1
|
|
223
|
-
px-1.5
|
|
224
|
-
py-1
|
|
225
|
-
rounded-md
|
|
226
|
-
${config.badgeBg}
|
|
227
|
-
`
|
|
228
|
-
.replace(/\s+/g, ' ')
|
|
229
|
-
.trim()}
|
|
230
|
-
>
|
|
231
|
-
<StatusIcon className={config.badgeText} />
|
|
232
|
-
<span
|
|
233
|
-
className={`
|
|
234
|
-
text-xs
|
|
235
|
-
leading-4
|
|
236
|
-
${config.badgeText}
|
|
237
|
-
`
|
|
238
|
-
.replace(/\s+/g, ' ')
|
|
239
|
-
.trim()}
|
|
240
|
-
>
|
|
241
|
-
{config.badgeLabel}
|
|
242
|
-
</span>
|
|
243
|
-
</div>
|
|
244
|
-
</div>
|
|
245
|
-
</button>
|
|
246
|
-
);
|
|
247
|
-
};
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { POSLocationButtonProps } from './POSLocationButton.types';
|
|
3
|
+
import { CheckIcon, XMarkIcon, CalendarIcon, NoSymbolIcon, UsersIcon } from './icons';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* POSLocationButton del sistema de diseño Siesa
|
|
7
|
+
*
|
|
8
|
+
* Botón especializado para punto de venta (POS) que muestra el estado de disponibilidad
|
|
9
|
+
* de una ubicación física (mesa, salón, terraza, etc.) con indicadores visuales claros.
|
|
10
|
+
*
|
|
11
|
+
* **Estados de disponibilidad (status):**
|
|
12
|
+
* - `available`: Disponible (verde/lime) con icono ✓
|
|
13
|
+
* - `occupied`: Ocupada (naranja/yellow) con icono ✗
|
|
14
|
+
* - `reserved`: Reservada (morado/purple) con icono 📅
|
|
15
|
+
* - `outOfService`: Fuera de servicio (gris/zinc) con icono 🚫
|
|
16
|
+
*
|
|
17
|
+
* **Estados visuales (state):**
|
|
18
|
+
* - `enabled`: Estado normal con fondo blanco/dark
|
|
19
|
+
* - `actived`: Estado seleccionado con fondo coloreado
|
|
20
|
+
*
|
|
21
|
+
* **Características:**
|
|
22
|
+
* - Ancho flexible: por defecto ocupa el 100% del contenedor (fullWidth=true)
|
|
23
|
+
* - Altura fija: 68px (optimizado para interfaces POS)
|
|
24
|
+
* - Muestra nombre de ubicación y capacidad (ej: "1/8 mesas")
|
|
25
|
+
* - Badge visual con icono y texto del estado
|
|
26
|
+
* - Dark mode completo en todos los estados
|
|
27
|
+
* - Focus ring adaptativo
|
|
28
|
+
* - Transiciones suaves
|
|
29
|
+
*
|
|
30
|
+
* Mejores prácticas implementadas:
|
|
31
|
+
* - Orden de modificadores: {responsive}:{dark}:{state}:{utility}
|
|
32
|
+
* - Dark mode con estrategia 'class' (darkMode: 'class')
|
|
33
|
+
* - Tokens de color consistentes con la documentación
|
|
34
|
+
* - Type safety con TypeScript estricto
|
|
35
|
+
* - Accesibilidad con ARIA labels
|
|
36
|
+
*
|
|
37
|
+
* @see docs/colors.md - Sistema de colores
|
|
38
|
+
* @see docs/typography.md - Sistema tipográfico
|
|
39
|
+
* @see docs/spacing.md - Sistema de espaciado
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* // Ubicación disponible
|
|
44
|
+
* <POSLocationButton
|
|
45
|
+
* locationName="Antejardín"
|
|
46
|
+
* status="available"
|
|
47
|
+
* capacity={{ current: 1, total: 8 }}
|
|
48
|
+
* onClick={() => console.log('Seleccionado')}
|
|
49
|
+
* />
|
|
50
|
+
*
|
|
51
|
+
* // Ubicación ocupada y seleccionada
|
|
52
|
+
* <POSLocationButton
|
|
53
|
+
* locationName="Terraza"
|
|
54
|
+
* status="occupied"
|
|
55
|
+
* state="actived"
|
|
56
|
+
* capacity={{ current: 5, total: 8 }}
|
|
57
|
+
* />
|
|
58
|
+
*
|
|
59
|
+
* // Ubicación reservada
|
|
60
|
+
* <POSLocationButton
|
|
61
|
+
* locationName="Salón Principal"
|
|
62
|
+
* status="reserved"
|
|
63
|
+
* capacity={{ current: 3, total: 12 }}
|
|
64
|
+
* />
|
|
65
|
+
*
|
|
66
|
+
* // Fuera de servicio
|
|
67
|
+
* <POSLocationButton
|
|
68
|
+
* locationName="Bar"
|
|
69
|
+
* status="outOfService"
|
|
70
|
+
* disabled
|
|
71
|
+
* />
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export const POSLocationButton: React.FC<POSLocationButtonProps> = ({
|
|
75
|
+
locationName,
|
|
76
|
+
status = 'available',
|
|
77
|
+
state = 'enabled',
|
|
78
|
+
capacity,
|
|
79
|
+
onClick,
|
|
80
|
+
disabled = false,
|
|
81
|
+
className = '',
|
|
82
|
+
ariaLabel,
|
|
83
|
+
fullWidth = true,
|
|
84
|
+
}) => {
|
|
85
|
+
// ===== CONFIGURACIÓN DE COLORES POR STATUS =====
|
|
86
|
+
// Mapeo de colores según especificaciones de Figma (pixel-perfect)
|
|
87
|
+
const statusConfig = {
|
|
88
|
+
available: {
|
|
89
|
+
// Azul Primary Custom (según Figma: #0e79fd)
|
|
90
|
+
textColor: 'text-primary-custom-600 dark:text-primary-custom-600',
|
|
91
|
+
badgeBg: 'bg-primary-custom-100 dark:bg-blue-900/30',
|
|
92
|
+
badgeText: 'text-primary-custom-600 dark:text-blue-400',
|
|
93
|
+
activedBg: 'bg-primary-custom-100 dark:bg-blue-900/30',
|
|
94
|
+
icon: CheckIcon,
|
|
95
|
+
badgeLabel: 'Disponible',
|
|
96
|
+
},
|
|
97
|
+
occupied: {
|
|
98
|
+
// Naranja/Yellow (según Figma: #af460e para texto, #fcedc9 para fondo)
|
|
99
|
+
textColor: 'text-[#af460e] dark:text-orange-400',
|
|
100
|
+
badgeBg: 'bg-[#fcedc9] dark:bg-yellow-900/30',
|
|
101
|
+
badgeText: 'text-[#af460e] dark:text-orange-400',
|
|
102
|
+
activedBg: 'bg-[#fcedc9] dark:bg-yellow-900/30',
|
|
103
|
+
icon: XMarkIcon,
|
|
104
|
+
badgeLabel: 'Ocupada',
|
|
105
|
+
},
|
|
106
|
+
reserved: {
|
|
107
|
+
// Morado/Purple (según Figma: #7e22ce para texto, #f3e8ff para fondo)
|
|
108
|
+
textColor: 'text-[#7e22ce] dark:text-fuchsia-400',
|
|
109
|
+
badgeBg: 'bg-[#f3e8ff] dark:bg-purple-900/30',
|
|
110
|
+
badgeText: 'text-[#7e22ce] dark:text-purple-400',
|
|
111
|
+
activedBg: 'bg-[#f3e8ff] dark:bg-purple-900/30',
|
|
112
|
+
icon: CalendarIcon,
|
|
113
|
+
badgeLabel: 'Reservada',
|
|
114
|
+
},
|
|
115
|
+
outOfService: {
|
|
116
|
+
// Gris/Zinc (según Figma: #3f3f46 para texto, #f4f4f5 para fondo)
|
|
117
|
+
textColor: 'text-[#3f3f46] dark:text-zinc-400',
|
|
118
|
+
badgeBg: 'bg-[#f4f4f5] dark:bg-zinc-800/30',
|
|
119
|
+
badgeText: 'text-content-tertiary dark:text-zinc-400',
|
|
120
|
+
activedBg: 'bg-[#f4f4f5] dark:bg-zinc-800/30',
|
|
121
|
+
icon: NoSymbolIcon,
|
|
122
|
+
badgeLabel: 'F. de Servicio',
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const config = statusConfig[status];
|
|
127
|
+
const StatusIcon = config.icon;
|
|
128
|
+
|
|
129
|
+
// ===== CLASES DE FONDO SEGÚN STATE =====
|
|
130
|
+
const backgroundClasses =
|
|
131
|
+
state === 'actived'
|
|
132
|
+
? config.activedBg // Fondo coloreado cuando está activo
|
|
133
|
+
: 'bg-white dark:bg-dark-bg-primary'; // Fondo blanco/dark cuando está enabled
|
|
134
|
+
|
|
135
|
+
// ===== CLASES DE ANCHO =====
|
|
136
|
+
const widthClasses = fullWidth ? 'w-full' : 'w-[189px]';
|
|
137
|
+
|
|
138
|
+
// ===== CLASES BASE =====
|
|
139
|
+
const baseClasses = `
|
|
140
|
+
flex
|
|
141
|
+
flex-col
|
|
142
|
+
items-stretch
|
|
143
|
+
justify-between
|
|
144
|
+
${widthClasses}
|
|
145
|
+
h-[68px]
|
|
146
|
+
p-2
|
|
147
|
+
rounded-lg
|
|
148
|
+
cursor-pointer
|
|
149
|
+
transition-all
|
|
150
|
+
duration-150
|
|
151
|
+
focus:outline-none
|
|
152
|
+
focus:ring-2
|
|
153
|
+
focus:ring-primary-custom-400
|
|
154
|
+
focus:ring-offset-2
|
|
155
|
+
dark:focus:ring-dark-border-custom
|
|
156
|
+
dark:focus:ring-offset-dark-bg-primary
|
|
157
|
+
hover:shadow-md
|
|
158
|
+
active:scale-[0.98]
|
|
159
|
+
disabled:opacity-50
|
|
160
|
+
disabled:cursor-not-allowed
|
|
161
|
+
disabled:pointer-events-none
|
|
162
|
+
`;
|
|
163
|
+
|
|
164
|
+
// ===== COMBINAR CLASES =====
|
|
165
|
+
const buttonClasses = [baseClasses, backgroundClasses, className]
|
|
166
|
+
.join(' ')
|
|
167
|
+
.replace(/\s+/g, ' ')
|
|
168
|
+
.trim();
|
|
169
|
+
|
|
170
|
+
// ===== FORMATEAR TEXTO DE CAPACIDAD =====
|
|
171
|
+
const capacityText = capacity
|
|
172
|
+
? `${capacity.current}/${capacity.total} mesas`
|
|
173
|
+
: null;
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<button
|
|
177
|
+
className={buttonClasses}
|
|
178
|
+
onClick={onClick}
|
|
179
|
+
disabled={disabled}
|
|
180
|
+
aria-label={ariaLabel || `${locationName} - ${config.badgeLabel}`}
|
|
181
|
+
type="button"
|
|
182
|
+
>
|
|
183
|
+
{/* ===== TÍTULO (Nombre de ubicación) ===== */}
|
|
184
|
+
<div
|
|
185
|
+
className={`
|
|
186
|
+
text-base
|
|
187
|
+
font-bold
|
|
188
|
+
leading-6
|
|
189
|
+
${config.textColor}
|
|
190
|
+
`
|
|
191
|
+
.replace(/\s+/g, ' ')
|
|
192
|
+
.trim()}
|
|
193
|
+
>
|
|
194
|
+
{locationName}
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
{/* ===== CONTENIDO (Capacidad + Badge) ===== */}
|
|
198
|
+
<div className="flex items-center justify-between">
|
|
199
|
+
{/* Capacidad (izquierda) */}
|
|
200
|
+
{capacityText && (
|
|
201
|
+
<div className="flex items-center gap-1">
|
|
202
|
+
<UsersIcon className={config.textColor} />
|
|
203
|
+
<span
|
|
204
|
+
className={`
|
|
205
|
+
text-[10px]
|
|
206
|
+
leading-[12px]
|
|
207
|
+
${config.textColor}
|
|
208
|
+
`
|
|
209
|
+
.replace(/\s+/g, ' ')
|
|
210
|
+
.trim()}
|
|
211
|
+
>
|
|
212
|
+
{capacityText}
|
|
213
|
+
</span>
|
|
214
|
+
</div>
|
|
215
|
+
)}
|
|
216
|
+
|
|
217
|
+
{/* Badge (derecha) */}
|
|
218
|
+
<div
|
|
219
|
+
className={`
|
|
220
|
+
inline-flex
|
|
221
|
+
items-center
|
|
222
|
+
gap-1
|
|
223
|
+
px-1.5
|
|
224
|
+
py-1
|
|
225
|
+
rounded-md
|
|
226
|
+
${config.badgeBg}
|
|
227
|
+
`
|
|
228
|
+
.replace(/\s+/g, ' ')
|
|
229
|
+
.trim()}
|
|
230
|
+
>
|
|
231
|
+
<StatusIcon className={config.badgeText} />
|
|
232
|
+
<span
|
|
233
|
+
className={`
|
|
234
|
+
text-xs
|
|
235
|
+
leading-4
|
|
236
|
+
${config.badgeText}
|
|
237
|
+
`
|
|
238
|
+
.replace(/\s+/g, ' ')
|
|
239
|
+
.trim()}
|
|
240
|
+
>
|
|
241
|
+
{config.badgeLabel}
|
|
242
|
+
</span>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
</button>
|
|
246
|
+
);
|
|
247
|
+
};
|
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
import type { MouseEvent } from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Estado de disponibilidad de la ubicación POS
|
|
5
|
-
* - available: Disponible (verde/lime)
|
|
6
|
-
* - occupied: Ocupada (naranja/yellow)
|
|
7
|
-
* - reserved: Reservada (morado/purple)
|
|
8
|
-
* - outOfService: Fuera de servicio (gris/zinc)
|
|
9
|
-
*/
|
|
10
|
-
export type POSLocationStatus = 'available' | 'occupied' | 'reserved' | 'outOfService';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Estado visual del botón
|
|
14
|
-
* - enabled: Estado normal (fondo blanco/dark-bg-primary)
|
|
15
|
-
* - actived: Estado activo/seleccionado (fondo con color del badge)
|
|
16
|
-
*/
|
|
17
|
-
export type POSLocationState = 'enabled' | 'actived';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Información de capacidad de la ubicación
|
|
21
|
-
*/
|
|
22
|
-
export interface POSLocationCapacity {
|
|
23
|
-
/**
|
|
24
|
-
* Número actual de mesas/espacios ocupados
|
|
25
|
-
*/
|
|
26
|
-
current: number;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Total de mesas/espacios disponibles
|
|
30
|
-
*/
|
|
31
|
-
total: number;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Props del componente POSLocationButton
|
|
36
|
-
*/
|
|
37
|
-
export interface POSLocationButtonProps {
|
|
38
|
-
/**
|
|
39
|
-
* Nombre de la ubicación (ej: "Antejardín", "Terraza", "Salón Principal")
|
|
40
|
-
*/
|
|
41
|
-
locationName: string;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Estado de disponibilidad de la ubicación
|
|
45
|
-
* @default 'available'
|
|
46
|
-
*/
|
|
47
|
-
status?: POSLocationStatus;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Estado visual del botón (enabled o actived)
|
|
51
|
-
* @default 'enabled'
|
|
52
|
-
*/
|
|
53
|
-
state?: POSLocationState;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Información de capacidad (mesas ocupadas/total)
|
|
57
|
-
* Ejemplo: { current: 1, total: 8 } → "1/8 mesas"
|
|
58
|
-
*/
|
|
59
|
-
capacity?: POSLocationCapacity;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Función a ejecutar al hacer clic en el botón
|
|
63
|
-
*/
|
|
64
|
-
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Si el botón está deshabilitado
|
|
68
|
-
* @default false
|
|
69
|
-
*/
|
|
70
|
-
disabled?: boolean;
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Clases CSS adicionales
|
|
74
|
-
*/
|
|
75
|
-
className?: string;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Label para accesibilidad (ARIA)
|
|
79
|
-
*/
|
|
80
|
-
ariaLabel?: string;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Si el botón ocupa todo el ancho disponible del contenedor padre
|
|
84
|
-
* @default true
|
|
85
|
-
*/
|
|
86
|
-
fullWidth?: boolean;
|
|
87
|
-
}
|
|
1
|
+
import type { MouseEvent } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Estado de disponibilidad de la ubicación POS
|
|
5
|
+
* - available: Disponible (verde/lime)
|
|
6
|
+
* - occupied: Ocupada (naranja/yellow)
|
|
7
|
+
* - reserved: Reservada (morado/purple)
|
|
8
|
+
* - outOfService: Fuera de servicio (gris/zinc)
|
|
9
|
+
*/
|
|
10
|
+
export type POSLocationStatus = 'available' | 'occupied' | 'reserved' | 'outOfService';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Estado visual del botón
|
|
14
|
+
* - enabled: Estado normal (fondo blanco/dark-bg-primary)
|
|
15
|
+
* - actived: Estado activo/seleccionado (fondo con color del badge)
|
|
16
|
+
*/
|
|
17
|
+
export type POSLocationState = 'enabled' | 'actived';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Información de capacidad de la ubicación
|
|
21
|
+
*/
|
|
22
|
+
export interface POSLocationCapacity {
|
|
23
|
+
/**
|
|
24
|
+
* Número actual de mesas/espacios ocupados
|
|
25
|
+
*/
|
|
26
|
+
current: number;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Total de mesas/espacios disponibles
|
|
30
|
+
*/
|
|
31
|
+
total: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Props del componente POSLocationButton
|
|
36
|
+
*/
|
|
37
|
+
export interface POSLocationButtonProps {
|
|
38
|
+
/**
|
|
39
|
+
* Nombre de la ubicación (ej: "Antejardín", "Terraza", "Salón Principal")
|
|
40
|
+
*/
|
|
41
|
+
locationName: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Estado de disponibilidad de la ubicación
|
|
45
|
+
* @default 'available'
|
|
46
|
+
*/
|
|
47
|
+
status?: POSLocationStatus;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Estado visual del botón (enabled o actived)
|
|
51
|
+
* @default 'enabled'
|
|
52
|
+
*/
|
|
53
|
+
state?: POSLocationState;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Información de capacidad (mesas ocupadas/total)
|
|
57
|
+
* Ejemplo: { current: 1, total: 8 } → "1/8 mesas"
|
|
58
|
+
*/
|
|
59
|
+
capacity?: POSLocationCapacity;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Función a ejecutar al hacer clic en el botón
|
|
63
|
+
*/
|
|
64
|
+
onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Si el botón está deshabilitado
|
|
68
|
+
* @default false
|
|
69
|
+
*/
|
|
70
|
+
disabled?: boolean;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Clases CSS adicionales
|
|
74
|
+
*/
|
|
75
|
+
className?: string;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Label para accesibilidad (ARIA)
|
|
79
|
+
*/
|
|
80
|
+
ariaLabel?: string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Si el botón ocupa todo el ancho disponible del contenedor padre
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
fullWidth?: boolean;
|
|
87
|
+
}
|