@infinitech.maps/st-map 1.0.19 → 1.0.20

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 (2) hide show
  1. package/README.md +284 -74
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -10,7 +10,33 @@ npm install @infinitech.maps/st-map
10
10
 
11
11
  **Peer dependencies:** `react >=18` y `react-dom >=18`.
12
12
 
13
- ## Uso básico
13
+ ## Inicio rápido
14
+
15
+ ### Modo self-contained (recomendado)
16
+
17
+ El componente carga el mapa automáticamente desde la API usando `apiKey` y `cacheKey`:
18
+
19
+ ```jsx
20
+ import { STMap } from "@infinitech.maps/st-map";
21
+
22
+ function App() {
23
+ return (
24
+ <STMap
25
+ apiKey="tu-api-key"
26
+ cacheKey="uuid-del-mapa"
27
+ onSeatClick={(seat, shape, event, meta) => {
28
+ console.log(seat.id, seat.price);
29
+ }}
30
+ loadingFallback={<p>Cargando mapa...</p>}
31
+ errorFallback={(error) => <p>Error: {error.message}</p>}
32
+ />
33
+ );
34
+ }
35
+ ```
36
+
37
+ ### Modo legacy (datos directos)
38
+
39
+ Para pasar el JSON del mapa directamente:
14
40
 
15
41
  ```jsx
16
42
  import { STMap } from "@infinitech.maps/st-map";
@@ -19,86 +45,128 @@ function App() {
19
45
  return (
20
46
  <STMap
21
47
  map={mapData}
22
- onSeatClick={(seat, shape, event) => {
23
- console.log(seat, shape);
48
+ onSeatClick={(seat, shape, event, meta) => {
49
+ console.log(seat.id, seat.price);
24
50
  }}
25
51
  />
26
52
  );
27
53
  }
28
54
  ```
29
55
 
56
+ ---
57
+
30
58
  ## Props
31
59
 
60
+ ### Modo self-contained
61
+
62
+ | Prop | Tipo | Default | Descripción |
63
+ |------|------|---------|-------------|
64
+ | `apiKey` | `string` | — | **Requerido.** API key de autenticación. |
65
+ | `cacheKey` | `string` | — | **Requerido.** UUID del mapa en caché. |
66
+ | `baseUrl` | `string` | `"http://localhost:3001"` | URL base del Gateway. |
67
+ | `loadingFallback` | `ReactNode` | `null` | Componente a mostrar mientras carga el mapa. |
68
+ | `errorFallback` | `ReactNode \| (error) => ReactNode` | `null` | Componente o función a mostrar en caso de error. |
69
+
70
+ ### Modo legacy
71
+
32
72
  | Prop | Tipo | Descripción |
33
73
  |------|------|-------------|
34
- | `map` | `object` | **Requerido.** Objeto JSON del mapa (formato v2 o legacy). |
35
- | `onSeatClick` | `(seat, shape, event) => void` | Callback al hacer clic en un asiento, silla o mesa. |
36
- | `onSectionClick` | `(shape, event) => void` | Callback al hacer clic en una sección/categoría completa. |
37
- | `prices` | `Record<string, number>` | Mapa de precios por UUID (ver sección de precios). |
38
- | `selectedSeats` | `string[]` | Lista de UUIDs seleccionados (modo controlado). |
39
- | `reservedSeats` | `string[]` | Lista de UUIDs de asientos reservados. |
40
- | `selectedSeatColor` | `string` | Color de asientos seleccionados. Default: `"#01FFFF"`. |
41
- | `reservedSeatColor` | `string` | Color de asientos reservados. Default: `"#FF0000"`. |
42
- | `purchasedSeatColor` | `string` | Color de asientos comprados. Default: `"#000000"`. |
43
- | `hoverColor` | `string` | Color al pasar el cursor. Default: `"#444"`. |
44
- | `hoverTransition` | `number` | Duración de la transición hover en ms. Default: `0`. |
45
- | `onReady` | `() => void` | Callback cuando el mapa termina de renderizar. |
74
+ | `map` | `object` | Objeto JSON del mapa (formato v2 o legacy). |
75
+
76
+ ### Callbacks
77
+
78
+ | Prop | Firma | Descripción |
79
+ |------|-------|-------------|
80
+ | `onSeatClick` | `(seat, shape, event, meta) => void` | Clic en un asiento, silla o mesa. |
81
+ | `onSectionClick` | `(shape, event) => void` | Clic en una sección/categoría completa. |
82
+ | `onEmptyAreaClick` | `() => void` | Clic en el área vacía del canvas. |
83
+ | `onReady` | `() => void` | El mapa terminó de renderizar. |
84
+
85
+ ### Personalización visual
86
+
87
+ | Prop | Tipo | Default | Descripción |
88
+ |------|------|---------|-------------|
89
+ | `prices` | `Record<string, number>` | — | Mapa de precios por UUID (ver [Resolución de precios](#resolución-jerárquica-de-precios)). |
90
+ | `selectedSeats` | `string[]` | — | Lista de UUIDs seleccionados (modo controlado). |
91
+ | `reservedSeats` | `string[]` | — | Lista de UUIDs de asientos reservados. |
92
+ | `selectedSeatColor` | `string` | `"#01FFFF"` | Color de asientos seleccionados. |
93
+ | `reservedSeatColor` | `string` | `"#FF0000"` | Color de asientos reservados. |
94
+ | `purchasedSeatColor` | `string` | `"#000000"` | Color de asientos comprados. |
95
+ | `hoverColor` | `string` | `"#444"` | Color al pasar el cursor. |
96
+ | `hoverTransition` | `number` | `0` | Duración de la transición hover en ms. |
46
97
 
47
98
  ---
48
99
 
49
- ## Obtener el UUID y precio de un elemento
100
+ ## Callbacks
50
101
 
51
- Al hacer clic en cualquier elemento reservable (asiento, silla o mesa), el SDK invoca el callback `onSeatClick` con tres argumentos. El **UUID** y el **precio** se obtienen del primer argumento (`seat`):
102
+ ### `onSeatClick(seat, shape, event, meta)`
52
103
 
53
- ### `onSeatClick(seat, shape, event)`
104
+ Se invoca al hacer clic en cualquier elemento reservable (asiento, silla o mesa).
54
105
 
55
106
  | Argumento | Descripción |
56
107
  |-----------|-------------|
57
- | `seat` | Objeto del asiento/mesa seleccionado. Contiene `id` (UUID) y `price` (si hay precio asignado). |
58
- | `shape` | Objeto de la forma padre (fila, mesa, sector). Contiene metadatos como `category`, `section`, `sectorName`, etc. |
108
+ | `seat` | Objeto del asiento/mesa seleccionado. Contiene `id` (UUID), `price`, `status`, etc. |
109
+ | `shape` | Objeto de la forma padre (fila, mesa, sector). Contiene `category`, `section`, `sectorName`, etc. |
59
110
  | `event` | Evento de Konva (canvas). |
111
+ | `meta` | Metadatos adicionales. Contiene `screenPosition` para posicionar popups. |
60
112
 
61
- #### Propiedades clave de `seat`
113
+ #### Propiedades de `seat`
62
114
 
63
115
  | Propiedad | Tipo | Descripción |
64
116
  |-----------|------|-------------|
65
117
  | `seat.id` | `string` | **UUID** único del elemento seleccionado. |
66
- | `seat.price` | `number \| undefined` | **Precio** resuelto. Presente solo si se pasó la prop `prices` y existe un precio para este elemento. Si no hay precio, esta propiedad **no existe**. |
118
+ | `seat.price` | `number \| undefined` | **Precio** resuelto. Presente solo si se pasó `prices` y existe un precio. |
67
119
  | `seat.name` | `string` | Nombre visible del asiento (ej. `"A01"`). |
68
120
  | `seat.row` | `string \| number` | Fila del asiento. |
69
121
  | `seat.column` | `number` | Columna del asiento. |
70
122
  | `seat.status` | `string` | Estado: `"vacant"`, `"reserved"`, `"purchased"`. |
71
- | `seat.isTable` | `boolean` | `true` cuando se selecciona una mesa completa (modo mesa). |
72
- | `seat.tableName` | `string` | Nombre de la mesa (solo si `isTable` es `true`). |
73
- | `seat.chairs` | `array` | Lista de sillas de la mesa (solo si `isTable` es `true`). |
74
- | `seat.totalChairs` | `number` | Total de sillas en la mesa (solo si `isTable` es `true`). |
123
+ | `seat.type` | `string` | Tipo de elemento: `"seat"` o `"table"`. |
124
+
125
+ ##### Propiedades exclusivas de mesas (`seat.type === "table"`)
126
+
127
+ | Propiedad | Tipo | Descripción |
128
+ |-----------|------|-------------|
129
+ | `seat.isTable` | `boolean` | `true` cuando es una mesa. |
130
+ | `seat.tableName` | `string` | Nombre de la mesa. |
131
+ | `seat.chairs` | `array` | Lista de sillas de la mesa. |
132
+ | `seat.totalChairs` | `number` | Total de sillas en la mesa. |
133
+ | `seat.vacantCount` | `number` | Cantidad de sillas vacantes. |
134
+ | `seat.reservedCount` | `number` | Cantidad de sillas reservadas. |
135
+ | `seat.purchasedCount` | `number` | Cantidad de sillas compradas. |
136
+ | `seat.available` | `number` | Alias de `vacantCount`. |
137
+
138
+ #### Propiedad `meta.screenPosition`
75
139
 
76
- ### Ejemplo: obtener UUID y precio
140
+ Contiene las coordenadas CSS del centro del elemento clicado, útil para posicionar tooltips o popups:
141
+
142
+ ```jsx
143
+ const handleSeatClick = (seat, shape, event, meta) => {
144
+ // Posicionar un popup sobre el asiento
145
+ setPopupPosition({
146
+ x: meta.screenPosition.x,
147
+ y: meta.screenPosition.y,
148
+ });
149
+ };
150
+ ```
151
+
152
+ ### Ejemplo completo: obtener UUID y precio
77
153
 
78
154
  ```jsx
79
155
  import { STMap } from "@infinitech.maps/st-map";
80
156
 
81
- function MapView({ mapData }) {
157
+ function MapView() {
82
158
  const prices = {
83
- // UUID de categoría, sector o asiento individual → precio
84
159
  "d5195a0a-2e6f-4167-a930-b26440d1e072": 150, // Categoría "VIP"
85
160
  "702fdfc0-3a10-48a5-861c-0ddddac1bf7f": 50, // Categoría "General"
86
161
  "58a5497e-eb75-445a-a89c-dc0fbe652d38": 500, // Asiento específico
87
162
  };
88
163
 
89
- const handleSeatClick = (seat, shape, event) => {
90
- // UUID del elemento
91
- const uuid = seat.id;
92
-
93
- // Precio (puede ser undefined si no se configuró)
94
- const precio = seat.price;
164
+ const handleSeatClick = (seat, shape, event, meta) => {
165
+ console.log("UUID:", seat.id);
166
+ console.log("Precio:", seat.price ?? "Sin precio asignado");
95
167
 
96
- console.log("UUID:", uuid);
97
- console.log("Precio:", precio !== undefined ? precio : "Sin precio asignado");
98
-
99
- // Ejemplo: agregar al carrito
100
- if (seat.isTable) {
101
- console.log(`Mesa "${seat.tableName}" con ${seat.totalChairs} sillas`);
168
+ if (seat.type === "table") {
169
+ console.log(`Mesa "${seat.tableName}" ${seat.vacantCount}/${seat.totalChairs} disponibles`);
102
170
  } else {
103
171
  console.log(`Asiento ${seat.name}, fila ${seat.row}`);
104
172
  }
@@ -106,7 +174,8 @@ function MapView({ mapData }) {
106
174
 
107
175
  return (
108
176
  <STMap
109
- map={mapData}
177
+ apiKey="tu-api-key"
178
+ cacheKey="uuid-del-mapa"
110
179
  prices={prices}
111
180
  onSeatClick={handleSeatClick}
112
181
  />
@@ -116,7 +185,7 @@ function MapView({ mapData }) {
116
185
 
117
186
  ### `onSectionClick(shape, event)`
118
187
 
119
- Para secciones/categorías completas (formas geométricas sin asientos individuales):
188
+ Se invoca al hacer clic en una sección/categoría completa (formas geométricas sin asientos individuales).
120
189
 
121
190
  | Propiedad | Tipo | Descripción |
122
191
  |-----------|------|-------------|
@@ -128,16 +197,15 @@ Para secciones/categorías completas (formas geométricas sin asientos individua
128
197
  | `shape.sectorName` | `string` | Nombre del sector. |
129
198
 
130
199
  ```jsx
131
- const handleSectionClick = (shape, event) => {
132
- console.log("UUID sección:", shape.id);
133
- console.log("Precio:", shape.price !== undefined ? shape.price : "Sin precio");
134
- };
135
-
136
200
  <STMap
137
- map={mapData}
201
+ apiKey="tu-api-key"
202
+ cacheKey="uuid-del-mapa"
138
203
  prices={prices}
139
204
  onSeatClick={handleSeatClick}
140
- onSectionClick={handleSectionClick}
205
+ onSectionClick={(shape, event) => {
206
+ console.log("UUID sección:", shape.id);
207
+ console.log("Precio:", shape.price ?? "Sin precio");
208
+ }}
141
209
  />
142
210
  ```
143
211
 
@@ -159,35 +227,57 @@ Si **no** se pasa la prop `prices`, no hay validación de precio y todos los ele
159
227
 
160
228
  ---
161
229
 
162
- ## Console log integrado
230
+ ## Métodos imperativos (ref)
163
231
 
164
- El SDK incluye un log automático en la consola del navegador cada vez que se hace clic en un elemento reservable:
232
+ Usa `ref` para acceder a métodos imperativos del componente:
165
233
 
166
- Para **asientos/mesas** (`onSeatClick`):
167
- ```js
168
- [ST-Map] Seat click {
169
- uuid: "58a5497e-eb75-445a-a89c-dc0fbe652d38",
170
- price: 500,
171
- row: "A",
172
- column: 1,
173
- category: "VIP",
174
- section: "Sector 1"
234
+ ```jsx
235
+ import { useRef } from "react";
236
+ import { STMap } from "@infinitech.maps/st-map";
237
+
238
+ function App() {
239
+ const mapRef = useRef();
240
+
241
+ return <STMap ref={mapRef} apiKey="tu-api-key" cacheKey="uuid-del-mapa" />;
175
242
  }
176
243
  ```
177
244
 
178
- Para **secciones** (`onSectionClick`):
179
- ```js
180
- [ST-Map] Section click {
181
- uuid: "3b1633c9-57d0-4438-b952-95c213f98e29",
182
- price: 200,
183
- category: "General",
184
- section: "Sector 2"
185
- }
245
+ ### `resolveReservationIds(selections)`
246
+
247
+ Convierte los elementos clicados en IDs de asientos/sillas individuales listos para la API. Para mesas, extrae los IDs de las sillas vacantes; para asientos, devuelve `[id]`.
248
+
249
+ ```jsx
250
+ const seatIds = mapRef.current.resolveReservationIds([
251
+ { id: "uuid-mesa", quantity: 3 }, // extrae 3 sillas vacantes
252
+ { id: "uuid-asiento" }, // devuelve [uuid-asiento]
253
+ ]);
254
+ // → ["silla-1", "silla-2", "silla-3", "uuid-asiento"]
186
255
  ```
187
256
 
188
- Si un campo no existe en el elemento (ej. fila/columna en mesas), se muestra `"N/A"`. Si no hay precio, se muestra `"sin precio"`.
257
+ | Parámetro | Tipo | Descripción |
258
+ |-----------|------|-------------|
259
+ | `selections` | `Array<{ id: string, quantity?: number }>` | Elementos a resolver. `quantity` solo aplica a mesas. |
260
+
261
+ ### `reserveSelected({ selections, ttlSeconds })`
262
+
263
+ Resuelve IDs y reserva en una sola llamada. Requiere `apiKey` y `cacheKey`.
264
+
265
+ ```jsx
266
+ const { seatIds } = await mapRef.current.reserveSelected({
267
+ selections: [{ id: "uuid-mesa", quantity: 2 }],
268
+ ttlSeconds: 300, // 5 minutos (default)
269
+ });
270
+ ```
189
271
 
190
- Esto facilita la depuración durante el desarrollo.
272
+ ### `releaseAll({ seatIds })`
273
+
274
+ Libera asientos (vuelven a estado `"vacant"`). Requiere `apiKey` y `cacheKey`.
275
+
276
+ ```jsx
277
+ await mapRef.current.releaseAll({
278
+ seatIds: ["silla-1", "silla-2"],
279
+ });
280
+ ```
191
281
 
192
282
  ---
193
283
 
@@ -207,7 +297,8 @@ const handleSeatClick = (seat) => {
207
297
  };
208
298
 
209
299
  <STMap
210
- map={mapData}
300
+ apiKey="tu-api-key"
301
+ cacheKey="uuid-del-mapa"
211
302
  selectedSeats={selected}
212
303
  onSeatClick={handleSeatClick}
213
304
  />
@@ -215,12 +306,131 @@ const handleSeatClick = (seat) => {
215
306
 
216
307
  ---
217
308
 
309
+ ## Console log integrado
310
+
311
+ El SDK incluye un log automático en la consola del navegador cada vez que se hace clic en un elemento reservable:
312
+
313
+ ```js
314
+ // Asientos/mesas
315
+ [ST-Map] Seat click {
316
+ uuid: "58a5497e-eb75-445a-a89c-dc0fbe652d38",
317
+ price: 500,
318
+ row: "A",
319
+ column: 1,
320
+ category: "VIP",
321
+ section: "Sector 1"
322
+ }
323
+
324
+ // Secciones
325
+ [ST-Map] Section click {
326
+ uuid: "3b1633c9-57d0-4438-b952-95c213f98e29",
327
+ price: 200,
328
+ category: "General",
329
+ section: "Sector 2"
330
+ }
331
+ ```
332
+
333
+ Si un campo no existe (ej. fila/columna en mesas), se muestra `"N/A"`. Sin precio se muestra `"sin precio"`.
334
+
335
+ ---
336
+
218
337
  ## STMapClient
219
338
 
220
- Cliente HTTP para interactuar con la API de gestión de asientos:
339
+ Cliente HTTP para interactuar con la API de gestión de asientos. Funciona de forma independiente, sin necesidad del componente React.
221
340
 
222
341
  ```js
223
342
  import { STMapClient } from "@infinitech.maps/st-map";
224
343
 
225
- const client = new STMapClient({ baseUrl: "https://api.example.com" });
344
+ const client = new STMapClient({ apiKey: "tu-api-key" });
345
+
346
+ // O con URL personalizada:
347
+ const client = new STMapClient({
348
+ apiKey: "tu-api-key",
349
+ baseUrl: "https://gateway.example.com",
350
+ });
351
+ ```
352
+
353
+ ### `getMap(cacheKey, options?)`
354
+
355
+ Carga el mapa desde la API.
356
+
357
+ ```js
358
+ const map = await client.getMap("uuid-del-mapa");
359
+ // Con versión específica:
360
+ const map = await client.getMap("uuid-del-mapa", { version: "v1" });
361
+ ```
362
+
363
+ ### `reserve({ cacheKey, seatIds, ttlSeconds?, version? })`
364
+
365
+ Reserva asientos. Estado → `"reserved"` con expiración.
366
+
367
+ ```js
368
+ const reserved = await client.reserve({
369
+ cacheKey: "uuid-del-mapa",
370
+ seatIds: ["seat-1", "seat-2"],
371
+ ttlSeconds: 300, // 5 minutos (default)
372
+ });
373
+ // → [{ id: "seat-1", status: "reserved", expiredAt: "..." }, ...]
374
+ ```
375
+
376
+ ### `purchase({ cacheKey, seatIds, version? })`
377
+
378
+ Compra asientos. Estado → `"purchased"` (permanente). Funciona sobre asientos vacantes o previamente reservados.
379
+
380
+ ```js
381
+ const purchased = await client.purchase({
382
+ cacheKey: "uuid-del-mapa",
383
+ seatIds: ["seat-1"],
384
+ });
385
+ // → [{ id: "seat-1", status: "purchased" }]
386
+ ```
387
+
388
+ ### `release({ cacheKey, seatIds, version? })`
389
+
390
+ Libera asientos. Estado → `"vacant"`. Idempotente: liberar un asiento ya vacante no genera error.
391
+
392
+ ```js
393
+ await client.release({
394
+ cacheKey: "uuid-del-mapa",
395
+ seatIds: ["seat-2"],
396
+ });
397
+ // → [{ id: "seat-2", status: "vacant" }]
398
+ ```
399
+
400
+ ### `getSeatStatus({ cacheKey, seatIds?, version? })`
401
+
402
+ Consulta el estado de asientos. Omitir `seatIds` devuelve **todos** los asientos del mapa.
403
+
404
+ ```js
405
+ // Estado de asientos específicos
406
+ const status = await client.getSeatStatus({
407
+ cacheKey: "uuid-del-mapa",
408
+ seatIds: ["seat-1", "seat-2"],
409
+ });
410
+ // → [{ id: "seat-1", status: "reserved", expiredAt: "..." }, ...]
411
+
412
+ // Estado de todos los asientos
413
+ const allStatus = await client.getSeatStatus({
414
+ cacheKey: "uuid-del-mapa",
415
+ });
416
+ ```
417
+
418
+ ### Métodos estáticos
419
+
420
+ #### `STMapClient.resolveReservationIds(element, quantity?)`
421
+
422
+ Resuelve los IDs individuales de un elemento devuelto por `onSeatClick`. Para mesas, extrae los IDs de las sillas vacantes; para asientos, devuelve `[id]`.
423
+
424
+ ```js
425
+ const ids = STMapClient.resolveReservationIds(clickedElement, 3);
426
+ await client.reserve({ cacheKey: "uuid-del-mapa", seatIds: ids });
427
+ ```
428
+
429
+ #### `STMapClient.vacantCount(element)`
430
+
431
+ Devuelve la cantidad de asientos vacantes para un elemento. Asientos individuales devuelven `0` o `1`; mesas devuelven la cantidad de sillas vacantes.
432
+
433
+ ```js
434
+ const available = STMapClient.vacantCount(clickedElement);
435
+ console.log(`${available} lugares disponibles`);
226
436
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infinitech.maps/st-map",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },