@zonetrix/shared 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -259,15 +259,32 @@ declare function downloadConfigAsFile(config: SeatMapConfig, filename?: string):
259
259
  */
260
260
 
261
261
  /**
262
- * Firebase seat state (excludes 'selected' which is client-only, 'hidden' which is filtered)
262
+ * Firebase seat state type (excludes 'selected' which is client-only, 'hidden' which is filtered)
263
263
  */
264
264
  type FirebaseSeatState = 'available' | 'reserved' | 'unavailable';
265
+ /**
266
+ * Seat state value stored in Firebase (for reserved/unavailable seats)
267
+ * Available seats: key is deleted (doesn't exist)
268
+ * Reserved/Unavailable seats: object with state, userId, and timestamp
269
+ */
270
+ interface FirebaseSeatStateValue {
271
+ state: 'reserved' | 'unavailable';
272
+ userId?: string;
273
+ timestamp?: number;
274
+ }
275
+ /**
276
+ * Entry in the seat states map - null/undefined means available
277
+ */
278
+ type FirebaseSeatStateEntry = FirebaseSeatStateValue | null;
265
279
  /**
266
280
  * Lightweight seat states map for high-frequency real-time updates
267
281
  * Stored at: seat_states/{seatMapId}
282
+ *
283
+ * Keys: encoded seat IDs
284
+ * Values: null = available, object = reserved/unavailable with user info
268
285
  */
269
286
  interface FirebaseSeatStates {
270
- [seatId: string]: FirebaseSeatState;
287
+ [seatId: string]: FirebaseSeatStateEntry;
271
288
  }
272
289
  /**
273
290
  * Position in Firebase (same structure as app)
@@ -368,6 +385,9 @@ interface FirebaseSeatStatesResult {
368
385
  loading: boolean;
369
386
  error: Error | null;
370
387
  lastUpdated: number | null;
388
+ myReservedSeats: string[];
389
+ otherReservedSeats: string[];
390
+ unavailableSeats: string[];
371
391
  }
372
392
  /**
373
393
  * Result from Firebase config hook
@@ -431,18 +451,28 @@ declare function toFirebaseSeatMap(config: SeatMapConfig, eventId: number, subEv
431
451
  declare function fromFirebaseSeatMap(firebase: FirebaseSeatMap, seatStates?: FirebaseSeatStates): SeatMapConfig;
432
452
  /**
433
453
  * Extract seat states from SeatMapConfig for the lightweight states node
454
+ *
455
+ * Only stores non-available states (reserved/unavailable).
456
+ * Available seats: key is not stored (viewer treats missing keys as available)
457
+ * Hidden seats: not stored (filtered out by viewer)
434
458
  */
435
459
  declare function extractSeatStates(config: SeatMapConfig): FirebaseSeatStates;
436
460
  /**
437
- * Merge Firebase seat states with locally-derived reserved/unavailable arrays
461
+ * Derive user-aware seat arrays from Firebase seat states
462
+ *
463
+ * @param states - Firebase seat states map
464
+ * @param currentUserId - Optional current user ID for user-aware derivation
465
+ * @returns Object with myReservedSeats, otherReservedSeats, and unavailableSeats arrays
438
466
  */
439
- declare function deriveSeatArraysFromStates(states: FirebaseSeatStates): {
440
- reservedSeats: string[];
467
+ declare function deriveSeatArraysFromStates(states: FirebaseSeatStates, currentUserId?: string): {
468
+ myReservedSeats: string[];
469
+ otherReservedSeats: string[];
441
470
  unavailableSeats: string[];
471
+ reservedSeats: string[];
442
472
  };
443
473
  /**
444
474
  * Create index updates for a seat map
445
475
  */
446
476
  declare function createIndexUpdates(seatMapId: string, eventId: number, subEventId: number): Record<string, boolean>;
447
477
 
448
- export { type AlignmentGuide, type BookingSelection, type CanvasConfig, type CanvasState, type ColorSettings, DEFAULT_COLORS, type EditorMode, type FirebaseCanvasConfig, type FirebaseConfigResult, type FirebaseHookOptions, type FirebaseIndexEntry, FirebasePaths, type FirebasePosition, type FirebaseSeat, type FirebaseSeatMap, type FirebaseSeatMapConfig, type FirebaseSeatMapMeta, type FirebaseSeatState, type FirebaseSeatStates, type FirebaseSeatStatesResult, type FirebaseStage, type ObjectConfig, type ObjectType, type RowPricing, type SeatData, type SeatMapConfig, type SeatMapMetadata, type SeatShape, type SeatState, type SectionConfig, type SerializedSeat, type SerializedSection, type SerializedStage, type StageConfig, type Tool, type ValidationResult, applySeatStateOverrides, calculateAvailableSeats, calculateCapacity, calculateSeatPrice, cloneConfig, createDefaultConfig, createIndexUpdates, decodeSeatId, deriveSeatArraysFromStates, downloadConfigAsFile, encodeSeatId, exportConfigAsJSON, extractSeatStates, formatDate, fromFirebaseSeatMap, fromFirebaseState, generateId, getSelectedSeats, importConfigFromJSON, toFirebaseSeatMap, toFirebaseState, updateConfigTimestamp, validateSeatMapConfig };
478
+ export { type AlignmentGuide, type BookingSelection, type CanvasConfig, type CanvasState, type ColorSettings, DEFAULT_COLORS, type EditorMode, type FirebaseCanvasConfig, type FirebaseConfigResult, type FirebaseHookOptions, type FirebaseIndexEntry, FirebasePaths, type FirebasePosition, type FirebaseSeat, type FirebaseSeatMap, type FirebaseSeatMapConfig, type FirebaseSeatMapMeta, type FirebaseSeatState, type FirebaseSeatStateEntry, type FirebaseSeatStateValue, type FirebaseSeatStates, type FirebaseSeatStatesResult, type FirebaseStage, type ObjectConfig, type ObjectType, type RowPricing, type SeatData, type SeatMapConfig, type SeatMapMetadata, type SeatShape, type SeatState, type SectionConfig, type SerializedSeat, type SerializedSection, type SerializedStage, type StageConfig, type Tool, type ValidationResult, applySeatStateOverrides, calculateAvailableSeats, calculateCapacity, calculateSeatPrice, cloneConfig, createDefaultConfig, createIndexUpdates, decodeSeatId, deriveSeatArraysFromStates, downloadConfigAsFile, encodeSeatId, exportConfigAsJSON, extractSeatStates, formatDate, fromFirebaseSeatMap, fromFirebaseState, generateId, getSelectedSeats, importConfigFromJSON, toFirebaseSeatMap, toFirebaseState, updateConfigTimestamp, validateSeatMapConfig };
package/dist/index.d.ts CHANGED
@@ -259,15 +259,32 @@ declare function downloadConfigAsFile(config: SeatMapConfig, filename?: string):
259
259
  */
260
260
 
261
261
  /**
262
- * Firebase seat state (excludes 'selected' which is client-only, 'hidden' which is filtered)
262
+ * Firebase seat state type (excludes 'selected' which is client-only, 'hidden' which is filtered)
263
263
  */
264
264
  type FirebaseSeatState = 'available' | 'reserved' | 'unavailable';
265
+ /**
266
+ * Seat state value stored in Firebase (for reserved/unavailable seats)
267
+ * Available seats: key is deleted (doesn't exist)
268
+ * Reserved/Unavailable seats: object with state, userId, and timestamp
269
+ */
270
+ interface FirebaseSeatStateValue {
271
+ state: 'reserved' | 'unavailable';
272
+ userId?: string;
273
+ timestamp?: number;
274
+ }
275
+ /**
276
+ * Entry in the seat states map - null/undefined means available
277
+ */
278
+ type FirebaseSeatStateEntry = FirebaseSeatStateValue | null;
265
279
  /**
266
280
  * Lightweight seat states map for high-frequency real-time updates
267
281
  * Stored at: seat_states/{seatMapId}
282
+ *
283
+ * Keys: encoded seat IDs
284
+ * Values: null = available, object = reserved/unavailable with user info
268
285
  */
269
286
  interface FirebaseSeatStates {
270
- [seatId: string]: FirebaseSeatState;
287
+ [seatId: string]: FirebaseSeatStateEntry;
271
288
  }
272
289
  /**
273
290
  * Position in Firebase (same structure as app)
@@ -368,6 +385,9 @@ interface FirebaseSeatStatesResult {
368
385
  loading: boolean;
369
386
  error: Error | null;
370
387
  lastUpdated: number | null;
388
+ myReservedSeats: string[];
389
+ otherReservedSeats: string[];
390
+ unavailableSeats: string[];
371
391
  }
372
392
  /**
373
393
  * Result from Firebase config hook
@@ -431,18 +451,28 @@ declare function toFirebaseSeatMap(config: SeatMapConfig, eventId: number, subEv
431
451
  declare function fromFirebaseSeatMap(firebase: FirebaseSeatMap, seatStates?: FirebaseSeatStates): SeatMapConfig;
432
452
  /**
433
453
  * Extract seat states from SeatMapConfig for the lightweight states node
454
+ *
455
+ * Only stores non-available states (reserved/unavailable).
456
+ * Available seats: key is not stored (viewer treats missing keys as available)
457
+ * Hidden seats: not stored (filtered out by viewer)
434
458
  */
435
459
  declare function extractSeatStates(config: SeatMapConfig): FirebaseSeatStates;
436
460
  /**
437
- * Merge Firebase seat states with locally-derived reserved/unavailable arrays
461
+ * Derive user-aware seat arrays from Firebase seat states
462
+ *
463
+ * @param states - Firebase seat states map
464
+ * @param currentUserId - Optional current user ID for user-aware derivation
465
+ * @returns Object with myReservedSeats, otherReservedSeats, and unavailableSeats arrays
438
466
  */
439
- declare function deriveSeatArraysFromStates(states: FirebaseSeatStates): {
440
- reservedSeats: string[];
467
+ declare function deriveSeatArraysFromStates(states: FirebaseSeatStates, currentUserId?: string): {
468
+ myReservedSeats: string[];
469
+ otherReservedSeats: string[];
441
470
  unavailableSeats: string[];
471
+ reservedSeats: string[];
442
472
  };
443
473
  /**
444
474
  * Create index updates for a seat map
445
475
  */
446
476
  declare function createIndexUpdates(seatMapId: string, eventId: number, subEventId: number): Record<string, boolean>;
447
477
 
448
- export { type AlignmentGuide, type BookingSelection, type CanvasConfig, type CanvasState, type ColorSettings, DEFAULT_COLORS, type EditorMode, type FirebaseCanvasConfig, type FirebaseConfigResult, type FirebaseHookOptions, type FirebaseIndexEntry, FirebasePaths, type FirebasePosition, type FirebaseSeat, type FirebaseSeatMap, type FirebaseSeatMapConfig, type FirebaseSeatMapMeta, type FirebaseSeatState, type FirebaseSeatStates, type FirebaseSeatStatesResult, type FirebaseStage, type ObjectConfig, type ObjectType, type RowPricing, type SeatData, type SeatMapConfig, type SeatMapMetadata, type SeatShape, type SeatState, type SectionConfig, type SerializedSeat, type SerializedSection, type SerializedStage, type StageConfig, type Tool, type ValidationResult, applySeatStateOverrides, calculateAvailableSeats, calculateCapacity, calculateSeatPrice, cloneConfig, createDefaultConfig, createIndexUpdates, decodeSeatId, deriveSeatArraysFromStates, downloadConfigAsFile, encodeSeatId, exportConfigAsJSON, extractSeatStates, formatDate, fromFirebaseSeatMap, fromFirebaseState, generateId, getSelectedSeats, importConfigFromJSON, toFirebaseSeatMap, toFirebaseState, updateConfigTimestamp, validateSeatMapConfig };
478
+ export { type AlignmentGuide, type BookingSelection, type CanvasConfig, type CanvasState, type ColorSettings, DEFAULT_COLORS, type EditorMode, type FirebaseCanvasConfig, type FirebaseConfigResult, type FirebaseHookOptions, type FirebaseIndexEntry, FirebasePaths, type FirebasePosition, type FirebaseSeat, type FirebaseSeatMap, type FirebaseSeatMapConfig, type FirebaseSeatMapMeta, type FirebaseSeatState, type FirebaseSeatStateEntry, type FirebaseSeatStateValue, type FirebaseSeatStates, type FirebaseSeatStatesResult, type FirebaseStage, type ObjectConfig, type ObjectType, type RowPricing, type SeatData, type SeatMapConfig, type SeatMapMetadata, type SeatShape, type SeatState, type SectionConfig, type SerializedSeat, type SerializedSection, type SerializedStage, type StageConfig, type Tool, type ValidationResult, applySeatStateOverrides, calculateAvailableSeats, calculateCapacity, calculateSeatPrice, cloneConfig, createDefaultConfig, createIndexUpdates, decodeSeatId, deriveSeatArraysFromStates, downloadConfigAsFile, encodeSeatId, exportConfigAsJSON, extractSeatStates, formatDate, fromFirebaseSeatMap, fromFirebaseState, generateId, getSelectedSeats, importConfigFromJSON, toFirebaseSeatMap, toFirebaseState, updateConfigTimestamp, validateSeatMapConfig };
package/dist/index.js CHANGED
@@ -390,12 +390,16 @@ function fromFirebaseSeatMap(firebase, seatStates) {
390
390
  const seats = Object.entries(firebase.seats || {}).map(
391
391
  ([encodedId, seat]) => {
392
392
  const id = decodeSeatId(encodedId);
393
- const state = seatStates?.[encodedId] ?? seatStates?.[id] ?? seat.state;
393
+ let effectiveState = seat.state;
394
+ const stateEntry = seatStates?.[encodedId] ?? seatStates?.[id];
395
+ if (stateEntry) {
396
+ effectiveState = stateEntry.state;
397
+ }
394
398
  return {
395
399
  id,
396
400
  position: seat.position,
397
401
  shape: seat.shape,
398
- state: fromFirebaseState(state),
402
+ state: fromFirebaseState(effectiveState),
399
403
  sectionName: seat.sectionName,
400
404
  rowLabel: seat.rowLabel,
401
405
  columnLabel: seat.columnLabel,
@@ -439,23 +443,39 @@ function extractSeatStates(config) {
439
443
  const states = {};
440
444
  for (const seat of config.seats) {
441
445
  if (seat.state === "hidden") continue;
446
+ if (seat.state === "available" || seat.state === "selected") continue;
442
447
  const encodedId = encodeSeatId(seat.id);
443
- states[encodedId] = toFirebaseState(seat.state);
448
+ states[encodedId] = {
449
+ state: seat.state,
450
+ timestamp: Date.now()
451
+ };
444
452
  }
445
453
  return states;
446
454
  }
447
- function deriveSeatArraysFromStates(states) {
448
- const reservedSeats = [];
455
+ function deriveSeatArraysFromStates(states, currentUserId) {
456
+ const myReservedSeats = [];
457
+ const otherReservedSeats = [];
449
458
  const unavailableSeats = [];
450
- for (const [encodedId, state] of Object.entries(states)) {
459
+ for (const [encodedId, entry] of Object.entries(states)) {
460
+ if (!entry) continue;
451
461
  const id = decodeSeatId(encodedId);
452
- if (state === "reserved") {
453
- reservedSeats.push(id);
454
- } else if (state === "unavailable") {
462
+ if (entry.state === "unavailable") {
455
463
  unavailableSeats.push(id);
464
+ } else if (entry.state === "reserved") {
465
+ if (currentUserId && entry.userId === currentUserId) {
466
+ myReservedSeats.push(id);
467
+ } else {
468
+ otherReservedSeats.push(id);
469
+ }
456
470
  }
457
471
  }
458
- return { reservedSeats, unavailableSeats };
472
+ return {
473
+ myReservedSeats,
474
+ otherReservedSeats,
475
+ unavailableSeats,
476
+ // Legacy: all reserved seats (for non-user-aware usage)
477
+ reservedSeats: [...myReservedSeats, ...otherReservedSeats]
478
+ };
459
479
  }
460
480
  function createIndexUpdates(seatMapId, eventId, subEventId) {
461
481
  return {
package/dist/index.mjs CHANGED
@@ -340,12 +340,16 @@ function fromFirebaseSeatMap(firebase, seatStates) {
340
340
  const seats = Object.entries(firebase.seats || {}).map(
341
341
  ([encodedId, seat]) => {
342
342
  const id = decodeSeatId(encodedId);
343
- const state = seatStates?.[encodedId] ?? seatStates?.[id] ?? seat.state;
343
+ let effectiveState = seat.state;
344
+ const stateEntry = seatStates?.[encodedId] ?? seatStates?.[id];
345
+ if (stateEntry) {
346
+ effectiveState = stateEntry.state;
347
+ }
344
348
  return {
345
349
  id,
346
350
  position: seat.position,
347
351
  shape: seat.shape,
348
- state: fromFirebaseState(state),
352
+ state: fromFirebaseState(effectiveState),
349
353
  sectionName: seat.sectionName,
350
354
  rowLabel: seat.rowLabel,
351
355
  columnLabel: seat.columnLabel,
@@ -389,23 +393,39 @@ function extractSeatStates(config) {
389
393
  const states = {};
390
394
  for (const seat of config.seats) {
391
395
  if (seat.state === "hidden") continue;
396
+ if (seat.state === "available" || seat.state === "selected") continue;
392
397
  const encodedId = encodeSeatId(seat.id);
393
- states[encodedId] = toFirebaseState(seat.state);
398
+ states[encodedId] = {
399
+ state: seat.state,
400
+ timestamp: Date.now()
401
+ };
394
402
  }
395
403
  return states;
396
404
  }
397
- function deriveSeatArraysFromStates(states) {
398
- const reservedSeats = [];
405
+ function deriveSeatArraysFromStates(states, currentUserId) {
406
+ const myReservedSeats = [];
407
+ const otherReservedSeats = [];
399
408
  const unavailableSeats = [];
400
- for (const [encodedId, state] of Object.entries(states)) {
409
+ for (const [encodedId, entry] of Object.entries(states)) {
410
+ if (!entry) continue;
401
411
  const id = decodeSeatId(encodedId);
402
- if (state === "reserved") {
403
- reservedSeats.push(id);
404
- } else if (state === "unavailable") {
412
+ if (entry.state === "unavailable") {
405
413
  unavailableSeats.push(id);
414
+ } else if (entry.state === "reserved") {
415
+ if (currentUserId && entry.userId === currentUserId) {
416
+ myReservedSeats.push(id);
417
+ } else {
418
+ otherReservedSeats.push(id);
419
+ }
406
420
  }
407
421
  }
408
- return { reservedSeats, unavailableSeats };
422
+ return {
423
+ myReservedSeats,
424
+ otherReservedSeats,
425
+ unavailableSeats,
426
+ // Legacy: all reserved seats (for non-user-aware usage)
427
+ reservedSeats: [...myReservedSeats, ...otherReservedSeats]
428
+ };
409
429
  }
410
430
  function createIndexUpdates(seatMapId, eventId, subEventId) {
411
431
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zonetrix/shared",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Shared types and utilities for seat-map-studio packages",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",