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.
Files changed (189) hide show
  1. package/README.md +115 -115
  2. package/bin/install.cjs +502 -502
  3. package/bin/prepare-publish.cjs +28 -28
  4. package/bin/restore-folders.cjs +28 -28
  5. package/claude/agents/siesa-ui-kit-specialist.md +2445 -0
  6. package/claude/prompts/component-template.md +121 -0
  7. package/claude/prompts/siesa-ui-kit.md +28 -0
  8. package/claude/settings.local.json +67 -2
  9. package/dist/components/Button/icons.d.ts +6 -5
  10. package/dist/components/Button/icons.d.ts.map +1 -1
  11. package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.d.ts.map +1 -1
  12. package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.types.d.ts +21 -0
  13. package/dist/components/DropdownItemCollapsible/DropdownItemCollapsible.types.d.ts.map +1 -1
  14. package/dist/components/NavigationRailCommercial/NavigationRailCommercial.d.ts +122 -0
  15. package/dist/components/NavigationRailCommercial/NavigationRailCommercial.d.ts.map +1 -0
  16. package/dist/components/NavigationRailCommercial/NavigationRailCommercial.types.d.ts +139 -0
  17. package/dist/components/NavigationRailCommercial/NavigationRailCommercial.types.d.ts.map +1 -0
  18. package/dist/components/NavigationRailCommercial/icons.d.ts +33 -0
  19. package/dist/components/NavigationRailCommercial/icons.d.ts.map +1 -0
  20. package/dist/components/NavigationRailCommercial/index.d.ts +4 -0
  21. package/dist/components/NavigationRailCommercial/index.d.ts.map +1 -0
  22. package/dist/components/NavigationRailItem/NavigationRailItem.d.ts.map +1 -1
  23. package/dist/components/NavigationRailItem/NavigationRailItem.types.d.ts +7 -0
  24. package/dist/components/NavigationRailItem/NavigationRailItem.types.d.ts.map +1 -1
  25. package/dist/components/NavigationRailTypes/NavigationRailTypes.d.ts.map +1 -1
  26. package/dist/components/NavigationRailTypes/NavigationRailTypes.types.d.ts +41 -0
  27. package/dist/components/NavigationRailTypes/NavigationRailTypes.types.d.ts.map +1 -1
  28. package/dist/components/NavigationRailTypes/icons.d.ts +15 -29
  29. package/dist/components/NavigationRailTypes/icons.d.ts.map +1 -1
  30. package/dist/components/Select/Select.d.ts.map +1 -1
  31. package/dist/components/Select/icons.d.ts +6 -2
  32. package/dist/components/Select/icons.d.ts.map +1 -1
  33. package/dist/index.d.ts +32 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/siesa-ui-kit.cjs +404 -190
  36. package/dist/siesa-ui-kit.cjs.map +1 -1
  37. package/dist/siesa-ui-kit.mjs +6590 -1506
  38. package/dist/siesa-ui-kit.mjs.map +1 -1
  39. package/dist/views/LayoutCommercial/LayoutCommercial.d.ts +48 -0
  40. package/dist/views/LayoutCommercial/LayoutCommercial.d.ts.map +1 -0
  41. package/dist/views/LayoutCommercial/LayoutCommercial.types.d.ts +49 -0
  42. package/dist/views/LayoutCommercial/LayoutCommercial.types.d.ts.map +1 -0
  43. package/dist/views/LayoutCommercial/index.d.ts +3 -0
  44. package/dist/views/LayoutCommercial/index.d.ts.map +1 -0
  45. package/docs/icons.md +12 -31
  46. package/package.json +111 -110
  47. package/src/components/Avatar/Avatar.stories.tsx +494 -494
  48. package/src/components/Button/Button.stories.tsx +950 -950
  49. package/src/components/Button/Button.tsx +337 -337
  50. package/src/components/Button/Button.types.ts +180 -180
  51. package/src/components/Button/icons.tsx +23 -62
  52. package/src/components/DescriptionList/DescriptionList.stories.tsx +250 -250
  53. package/src/components/Divider/Divider.stories.tsx +263 -263
  54. package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.stories.tsx +317 -317
  55. package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.tsx +307 -287
  56. package/src/components/DropdownItemCollapsible/DropdownItemCollapsible.types.ts +136 -111
  57. package/src/components/DropdownItemCollapsible/README.md +264 -264
  58. package/src/components/DropdownItemCollapsible/icons.tsx +57 -57
  59. package/src/components/DropdownItemCollapsible/index.ts +12 -12
  60. package/src/components/DropdownItemHeading/DropdownItemHeading.stories.tsx +386 -386
  61. package/src/components/DropdownItemHeading/DropdownItemHeading.tsx +216 -216
  62. package/src/components/DropdownItemHeading/DropdownItemHeading.types.ts +93 -93
  63. package/src/components/DropdownItemHeading/README.md +573 -573
  64. package/src/components/DropdownItemHeading/icons.tsx +125 -125
  65. package/src/components/DropdownItemHeading/index.ts +3 -3
  66. package/src/components/Input/Input.stories.tsx +583 -583
  67. package/src/components/LoginView/LoginView.stories.tsx +148 -148
  68. package/src/components/LoginView/LoginView.tsx +426 -426
  69. package/src/components/LoginView/LoginView.types.ts +52 -52
  70. package/src/components/LoginView/README.md +396 -396
  71. package/src/components/LoginView/icons.tsx +85 -85
  72. package/src/components/LoginView/index.ts +3 -3
  73. package/src/components/Navbar/Navbar.stories.tsx +810 -810
  74. package/src/components/Navbar/Navbar.tsx +755 -755
  75. package/src/components/Navbar/Navbar.types.ts +219 -219
  76. package/src/components/Navbar/README.md +279 -279
  77. package/src/components/Navbar/index.ts +8 -8
  78. package/src/components/NavigationRailCommercial/NavigationRailCommercial.stories.tsx +464 -0
  79. package/src/components/NavigationRailCommercial/NavigationRailCommercial.tsx +301 -0
  80. package/src/components/NavigationRailCommercial/NavigationRailCommercial.types.ts +162 -0
  81. package/src/components/NavigationRailCommercial/README.md +251 -0
  82. package/src/components/NavigationRailCommercial/icons.tsx +54 -0
  83. package/src/components/NavigationRailCommercial/index.ts +6 -0
  84. package/src/components/NavigationRailItem/NavigationRailItem.stories.tsx +667 -667
  85. package/src/components/NavigationRailItem/NavigationRailItem.tsx +314 -313
  86. package/src/components/NavigationRailItem/NavigationRailItem.types.ts +175 -167
  87. package/src/components/NavigationRailItem/README.md +476 -476
  88. package/src/components/NavigationRailItem/index.ts +2 -2
  89. package/src/components/NavigationRailPanel/NavigationRailPanel.stories.tsx +462 -462
  90. package/src/components/NavigationRailPanel/NavigationRailPanel.tsx +332 -332
  91. package/src/components/NavigationRailPanel/NavigationRailPanel.types.ts +178 -178
  92. package/src/components/NavigationRailPanel/README.md +461 -461
  93. package/src/components/NavigationRailPanel/index.ts +6 -6
  94. package/src/components/NavigationRailTypes/NavigationRailTypes.stories.tsx +682 -528
  95. package/src/components/NavigationRailTypes/NavigationRailTypes.tsx +363 -378
  96. package/src/components/NavigationRailTypes/NavigationRailTypes.types.ts +178 -130
  97. package/src/components/NavigationRailTypes/README.md +573 -573
  98. package/src/components/NavigationRailTypes/icons.tsx +76 -141
  99. package/src/components/NavigationRailTypes/index.ts +7 -7
  100. package/src/components/Notification/Notification.stories.tsx +513 -513
  101. package/src/components/Notification/Notification.tsx +145 -145
  102. package/src/components/Notification/Notification.types.ts +142 -142
  103. package/src/components/Notification/README.md +409 -409
  104. package/src/components/POSConvention/POSConvention.stories.tsx +235 -235
  105. package/src/components/POSConvention/POSConvention.tsx +129 -129
  106. package/src/components/POSConvention/POSConvention.types.ts +38 -38
  107. package/src/components/POSConvention/README.md +123 -123
  108. package/src/components/POSConvention/icons.tsx +45 -45
  109. package/src/components/POSConvention/index.ts +3 -3
  110. package/src/components/POSLocationButton/POSLocationButton.stories.tsx +531 -531
  111. package/src/components/POSLocationButton/POSLocationButton.tsx +247 -247
  112. package/src/components/POSLocationButton/POSLocationButton.types.ts +87 -87
  113. package/src/components/POSLocationButton/README.md +253 -253
  114. package/src/components/POSLocationButton/icons.tsx +120 -120
  115. package/src/components/POSLocationButton/index.ts +14 -14
  116. package/src/components/POSNumberButton/POSNumberButton.stories.tsx +415 -415
  117. package/src/components/POSNumberButton/POSNumberButton.tsx +179 -179
  118. package/src/components/POSNumberButton/POSNumberButton.types.ts +51 -51
  119. package/src/components/POSNumberButton/README.md +321 -321
  120. package/src/components/POSNumberButton/index.ts +3 -3
  121. package/src/components/POSProductButton/POSProductButton.stories.tsx +318 -318
  122. package/src/components/POSProductCard/POSProductCard.stories.tsx +642 -642
  123. package/src/components/POSProductCard/POSProductCard.tsx +208 -208
  124. package/src/components/POSProductCard/POSProductCard.types.ts +76 -76
  125. package/src/components/POSProductCard/README.md +179 -179
  126. package/src/components/POSProductCard/icons.tsx +26 -26
  127. package/src/components/POSProductCard/index.ts +2 -2
  128. package/src/components/POSProductSidebarItems/POSProductSidebarItems.stories.tsx +753 -753
  129. package/src/components/POSProductSidebarItems/POSProductSidebarItems.tsx +332 -332
  130. package/src/components/POSProductSidebarItems/POSProductSidebarItems.types.ts +119 -119
  131. package/src/components/POSProductSidebarItems/README.md +198 -198
  132. package/src/components/POSProductSidebarItems/icons.tsx +21 -21
  133. package/src/components/POSProductSidebarItems/index.ts +3 -3
  134. package/src/components/POSTable/POSTable.stories.tsx +737 -737
  135. package/src/components/POSTable/POSTable.tsx +401 -401
  136. package/src/components/POSTable/README.md +286 -286
  137. package/src/components/Quantity/Quantity.stories.tsx +457 -457
  138. package/src/components/Radio/Radio.stories.tsx +523 -523
  139. package/src/components/Radio/Radio.tsx +1 -1
  140. package/src/components/Select/Select.stories.tsx +32 -0
  141. package/src/components/Select/Select.tsx +457 -454
  142. package/src/components/Select/icons.tsx +16 -41
  143. package/src/components/SignUpView/SignUpView.stories.tsx +129 -129
  144. package/src/components/SignUpView/SignUpView.tsx +503 -503
  145. package/src/components/SignUpView/SignUpView.types.ts +58 -58
  146. package/src/components/SignUpView/icons.tsx +71 -71
  147. package/src/components/SignUpView/index.ts +3 -3
  148. package/src/components/Switch/README.md +112 -112
  149. package/src/components/Switch/Switch.stories.tsx +550 -550
  150. package/src/components/Switch/Switch.tsx +246 -246
  151. package/src/components/Switch/Switch.types.ts +67 -67
  152. package/src/components/Table/Table.stories.tsx +805 -805
  153. package/src/components/Tabs/README.md +201 -201
  154. package/src/components/Tabs/Tabs.stories.tsx +580 -580
  155. package/src/components/Tabs/Tabs.tsx +356 -356
  156. package/src/components/Tabs/Tabs.types.ts +127 -127
  157. package/src/components/Tabs/icons.tsx +129 -129
  158. package/src/components/Tabs/index.ts +11 -11
  159. package/src/components/Textarea/Textarea.stories.tsx +535 -535
  160. package/src/index.ts +133 -102
  161. package/src/views/LayoutCommercial/LayoutCommercial.stories.tsx +374 -0
  162. package/src/views/LayoutCommercial/LayoutCommercial.tsx +125 -0
  163. package/src/views/LayoutCommercial/LayoutCommercial.types.ts +54 -0
  164. package/src/views/LayoutCommercial/README.md +286 -0
  165. package/src/views/LayoutCommercial/index.ts +2 -0
  166. package/src/views/ListView/ListView.stories.tsx +329 -329
  167. package/src/views/ListView/ListView.tsx +570 -570
  168. package/src/views/ListView/ListView.types.ts +211 -211
  169. package/src/views/ListView/icons.tsx +282 -282
  170. package/src/views/ListView/index.ts +11 -11
  171. package/src/views/LoginView/LoginView.tsx +426 -426
  172. package/src/views/ProductsView/ProductsView.stories.tsx +344 -344
  173. package/src/views/ProductsView/ProductsView.tsx +480 -480
  174. package/src/views/ProductsView/ProductsView.types.ts +238 -238
  175. package/src/views/ProductsView/README.md +312 -312
  176. package/src/views/ProductsView/icons.tsx +38 -38
  177. package/src/views/ProductsView/index.ts +8 -8
  178. package/src/views/RecoverPasswordView/RecoverPasswordView.tsx +376 -376
  179. package/src/views/SignUpView/SignUpView.tsx +503 -503
  180. package/src/views/TableLayoutView/README.md +268 -268
  181. package/src/views/TableLayoutView/TableLayoutView.stories.tsx +235 -235
  182. package/src/views/TableLayoutView/TableLayoutView.tsx +461 -461
  183. package/src/views/TableLayoutView/TableLayoutView.types.ts +209 -209
  184. package/src/views/TableLayoutView/icons.tsx +113 -113
  185. package/src/views/TableLayoutView/index.ts +6 -6
  186. package/storybook/main.ts +19 -19
  187. package/storybook/preview.tsx +84 -84
  188. package/storybook/vitest.setup.ts +6 -6
  189. 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
+ }