@infinitech.maps/st-map
v1.0.20
Published
SDK de React para renderizar mapas interactivos de SmartTicket con selección de asientos, mesas y secciones.
Keywords
Readme
@infinitech.maps/st-map
SDK de React para renderizar mapas interactivos de SmartTicket con selección de asientos, mesas y secciones.
Instalación
npm install @infinitech.maps/st-mapPeer dependencies: react >=18 y react-dom >=18.
Inicio rápido
Modo self-contained (recomendado)
El componente carga el mapa automáticamente desde la API usando apiKey y cacheKey:
import { STMap } from "@infinitech.maps/st-map";
function App() {
return (
<STMap
apiKey="tu-api-key"
cacheKey="uuid-del-mapa"
onSeatClick={(seat, shape, event, meta) => {
console.log(seat.id, seat.price);
}}
loadingFallback={<p>Cargando mapa...</p>}
errorFallback={(error) => <p>Error: {error.message}</p>}
/>
);
}Modo legacy (datos directos)
Para pasar el JSON del mapa directamente:
import { STMap } from "@infinitech.maps/st-map";
function App() {
return (
<STMap
map={mapData}
onSeatClick={(seat, shape, event, meta) => {
console.log(seat.id, seat.price);
}}
/>
);
}Props
Modo self-contained
| Prop | Tipo | Default | Descripción |
|------|------|---------|-------------|
| apiKey | string | — | Requerido. API key de autenticación. |
| cacheKey | string | — | Requerido. UUID del mapa en caché. |
| baseUrl | string | "http://localhost:3001" | URL base del Gateway. |
| loadingFallback | ReactNode | null | Componente a mostrar mientras carga el mapa. |
| errorFallback | ReactNode \| (error) => ReactNode | null | Componente o función a mostrar en caso de error. |
Modo legacy
| Prop | Tipo | Descripción |
|------|------|-------------|
| map | object | Objeto JSON del mapa (formato v2 o legacy). |
Callbacks
| Prop | Firma | Descripción |
|------|-------|-------------|
| onSeatClick | (seat, shape, event, meta) => void | Clic en un asiento, silla o mesa. |
| onSectionClick | (shape, event) => void | Clic en una sección/categoría completa. |
| onEmptyAreaClick | () => void | Clic en el área vacía del canvas. |
| onReady | () => void | El mapa terminó de renderizar. |
Personalización visual
| Prop | Tipo | Default | Descripción |
|------|------|---------|-------------|
| prices | Record<string, number> | — | Mapa de precios por UUID (ver Resolución de precios). |
| selectedSeats | string[] | — | Lista de UUIDs seleccionados (modo controlado). |
| reservedSeats | string[] | — | Lista de UUIDs de asientos reservados. |
| selectedSeatColor | string | "#01FFFF" | Color de asientos seleccionados. |
| reservedSeatColor | string | "#FF0000" | Color de asientos reservados. |
| purchasedSeatColor | string | "#000000" | Color de asientos comprados. |
| hoverColor | string | "#444" | Color al pasar el cursor. |
| hoverTransition | number | 0 | Duración de la transición hover en ms. |
Callbacks
onSeatClick(seat, shape, event, meta)
Se invoca al hacer clic en cualquier elemento reservable (asiento, silla o mesa).
| Argumento | Descripción |
|-----------|-------------|
| seat | Objeto del asiento/mesa seleccionado. Contiene id (UUID), price, status, etc. |
| shape | Objeto de la forma padre (fila, mesa, sector). Contiene category, section, sectorName, etc. |
| event | Evento de Konva (canvas). |
| meta | Metadatos adicionales. Contiene screenPosition para posicionar popups. |
Propiedades de seat
| Propiedad | Tipo | Descripción |
|-----------|------|-------------|
| seat.id | string | UUID único del elemento seleccionado. |
| seat.price | number \| undefined | Precio resuelto. Presente solo si se pasó prices y existe un precio. |
| seat.name | string | Nombre visible del asiento (ej. "A01"). |
| seat.row | string \| number | Fila del asiento. |
| seat.column | number | Columna del asiento. |
| seat.status | string | Estado: "vacant", "reserved", "purchased". |
| seat.type | string | Tipo de elemento: "seat" o "table". |
Propiedades exclusivas de mesas (seat.type === "table")
| Propiedad | Tipo | Descripción |
|-----------|------|-------------|
| seat.isTable | boolean | true cuando es una mesa. |
| seat.tableName | string | Nombre de la mesa. |
| seat.chairs | array | Lista de sillas de la mesa. |
| seat.totalChairs | number | Total de sillas en la mesa. |
| seat.vacantCount | number | Cantidad de sillas vacantes. |
| seat.reservedCount | number | Cantidad de sillas reservadas. |
| seat.purchasedCount | number | Cantidad de sillas compradas. |
| seat.available | number | Alias de vacantCount. |
Propiedad meta.screenPosition
Contiene las coordenadas CSS del centro del elemento clicado, útil para posicionar tooltips o popups:
const handleSeatClick = (seat, shape, event, meta) => {
// Posicionar un popup sobre el asiento
setPopupPosition({
x: meta.screenPosition.x,
y: meta.screenPosition.y,
});
};Ejemplo completo: obtener UUID y precio
import { STMap } from "@infinitech.maps/st-map";
function MapView() {
const prices = {
"d5195a0a-2e6f-4167-a930-b26440d1e072": 150, // Categoría "VIP"
"702fdfc0-3a10-48a5-861c-0ddddac1bf7f": 50, // Categoría "General"
"58a5497e-eb75-445a-a89c-dc0fbe652d38": 500, // Asiento específico
};
const handleSeatClick = (seat, shape, event, meta) => {
console.log("UUID:", seat.id);
console.log("Precio:", seat.price ?? "Sin precio asignado");
if (seat.type === "table") {
console.log(`Mesa "${seat.tableName}" — ${seat.vacantCount}/${seat.totalChairs} disponibles`);
} else {
console.log(`Asiento ${seat.name}, fila ${seat.row}`);
}
};
return (
<STMap
apiKey="tu-api-key"
cacheKey="uuid-del-mapa"
prices={prices}
onSeatClick={handleSeatClick}
/>
);
}onSectionClick(shape, event)
Se invoca al hacer clic en una sección/categoría completa (formas geométricas sin asientos individuales).
| Propiedad | Tipo | Descripción |
|-----------|------|-------------|
| shape.id | string | UUID de la sección. |
| shape.price | number \| undefined | Precio resuelto (si existe). |
| shape.category | string | ID de la categoría. |
| shape.categoryName | string | Nombre de la categoría. |
| shape.section | string | ID del sector. |
| shape.sectorName | string | Nombre del sector. |
<STMap
apiKey="tu-api-key"
cacheKey="uuid-del-mapa"
prices={prices}
onSeatClick={handleSeatClick}
onSectionClick={(shape, event) => {
console.log("UUID sección:", shape.id);
console.log("Precio:", shape.price ?? "Sin precio");
}}
/>Resolución jerárquica de precios
Cuando se pasa la prop prices, el SDK resuelve el precio de forma jerárquica buscando en este orden:
- Asiento individual →
prices[seat.id] - Sector →
prices[shape.section] - Categoría →
prices[shape.category]
El primer match encontrado se asigna como seat.price (o shape.price en secciones).
Si se proporcionan prices pero no se encuentra precio en ningún nivel, el SDK muestra una alerta y no permite la selección del elemento.
Si no se pasa la prop prices, no hay validación de precio y todos los elementos son seleccionables.
Métodos imperativos (ref)
Usa ref para acceder a métodos imperativos del componente:
import { useRef } from "react";
import { STMap } from "@infinitech.maps/st-map";
function App() {
const mapRef = useRef();
return <STMap ref={mapRef} apiKey="tu-api-key" cacheKey="uuid-del-mapa" />;
}resolveReservationIds(selections)
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].
const seatIds = mapRef.current.resolveReservationIds([
{ id: "uuid-mesa", quantity: 3 }, // extrae 3 sillas vacantes
{ id: "uuid-asiento" }, // devuelve [uuid-asiento]
]);
// → ["silla-1", "silla-2", "silla-3", "uuid-asiento"]| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| selections | Array<{ id: string, quantity?: number }> | Elementos a resolver. quantity solo aplica a mesas. |
reserveSelected({ selections, ttlSeconds })
Resuelve IDs y reserva en una sola llamada. Requiere apiKey y cacheKey.
const { seatIds } = await mapRef.current.reserveSelected({
selections: [{ id: "uuid-mesa", quantity: 2 }],
ttlSeconds: 300, // 5 minutos (default)
});releaseAll({ seatIds })
Libera asientos (vuelven a estado "vacant"). Requiere apiKey y cacheKey.
await mapRef.current.releaseAll({
seatIds: ["silla-1", "silla-2"],
});Selección controlada
Por defecto el SDK gestiona la selección internamente. Para controlarla externamente, pasa selectedSeats:
const [selected, setSelected] = useState([]);
const handleSeatClick = (seat) => {
setSelected((prev) =>
prev.includes(seat.id)
? prev.filter((id) => id !== seat.id)
: [...prev, seat.id]
);
};
<STMap
apiKey="tu-api-key"
cacheKey="uuid-del-mapa"
selectedSeats={selected}
onSeatClick={handleSeatClick}
/>Console log integrado
El SDK incluye un log automático en la consola del navegador cada vez que se hace clic en un elemento reservable:
// Asientos/mesas
[ST-Map] Seat click {
uuid: "58a5497e-eb75-445a-a89c-dc0fbe652d38",
price: 500,
row: "A",
column: 1,
category: "VIP",
section: "Sector 1"
}
// Secciones
[ST-Map] Section click {
uuid: "3b1633c9-57d0-4438-b952-95c213f98e29",
price: 200,
category: "General",
section: "Sector 2"
}Si un campo no existe (ej. fila/columna en mesas), se muestra "N/A". Sin precio se muestra "sin precio".
STMapClient
Cliente HTTP para interactuar con la API de gestión de asientos. Funciona de forma independiente, sin necesidad del componente React.
import { STMapClient } from "@infinitech.maps/st-map";
const client = new STMapClient({ apiKey: "tu-api-key" });
// O con URL personalizada:
const client = new STMapClient({
apiKey: "tu-api-key",
baseUrl: "https://gateway.example.com",
});getMap(cacheKey, options?)
Carga el mapa desde la API.
const map = await client.getMap("uuid-del-mapa");
// Con versión específica:
const map = await client.getMap("uuid-del-mapa", { version: "v1" });reserve({ cacheKey, seatIds, ttlSeconds?, version? })
Reserva asientos. Estado → "reserved" con expiración.
const reserved = await client.reserve({
cacheKey: "uuid-del-mapa",
seatIds: ["seat-1", "seat-2"],
ttlSeconds: 300, // 5 minutos (default)
});
// → [{ id: "seat-1", status: "reserved", expiredAt: "..." }, ...]purchase({ cacheKey, seatIds, version? })
Compra asientos. Estado → "purchased" (permanente). Funciona sobre asientos vacantes o previamente reservados.
const purchased = await client.purchase({
cacheKey: "uuid-del-mapa",
seatIds: ["seat-1"],
});
// → [{ id: "seat-1", status: "purchased" }]release({ cacheKey, seatIds, version? })
Libera asientos. Estado → "vacant". Idempotente: liberar un asiento ya vacante no genera error.
await client.release({
cacheKey: "uuid-del-mapa",
seatIds: ["seat-2"],
});
// → [{ id: "seat-2", status: "vacant" }]getSeatStatus({ cacheKey, seatIds?, version? })
Consulta el estado de asientos. Omitir seatIds devuelve todos los asientos del mapa.
// Estado de asientos específicos
const status = await client.getSeatStatus({
cacheKey: "uuid-del-mapa",
seatIds: ["seat-1", "seat-2"],
});
// → [{ id: "seat-1", status: "reserved", expiredAt: "..." }, ...]
// Estado de todos los asientos
const allStatus = await client.getSeatStatus({
cacheKey: "uuid-del-mapa",
});Métodos estáticos
STMapClient.resolveReservationIds(element, quantity?)
Resuelve los IDs individuales de un elemento devuelto por onSeatClick. Para mesas, extrae los IDs de las sillas vacantes; para asientos, devuelve [id].
const ids = STMapClient.resolveReservationIds(clickedElement, 3);
await client.reserve({ cacheKey: "uuid-del-mapa", seatIds: ids });STMapClient.vacantCount(element)
Devuelve la cantidad de asientos vacantes para un elemento. Asientos individuales devuelven 0 o 1; mesas devuelven la cantidad de sillas vacantes.
const available = STMapClient.vacantCount(clickedElement);
console.log(`${available} lugares disponibles`);