@zonetrix/shared 2.2.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 +222 -1
- package/dist/index.d.ts +222 -1
- package/dist/index.js +231 -0
- package/dist/index.mjs +221 -0
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -254,4 +254,225 @@ declare function importConfigFromJSON(jsonString: string): SeatMapConfig;
|
|
|
254
254
|
*/
|
|
255
255
|
declare function downloadConfigAsFile(config: SeatMapConfig, filename?: string): void;
|
|
256
256
|
|
|
257
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Firebase Realtime Database type definitions for Seat Map Studio
|
|
259
|
+
*/
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Firebase seat state type (excludes 'selected' which is client-only, 'hidden' which is filtered)
|
|
263
|
+
*/
|
|
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;
|
|
279
|
+
/**
|
|
280
|
+
* Lightweight seat states map for high-frequency real-time updates
|
|
281
|
+
* Stored at: seat_states/{seatMapId}
|
|
282
|
+
*
|
|
283
|
+
* Keys: encoded seat IDs
|
|
284
|
+
* Values: null = available, object = reserved/unavailable with user info
|
|
285
|
+
*/
|
|
286
|
+
interface FirebaseSeatStates {
|
|
287
|
+
[seatId: string]: FirebaseSeatStateEntry;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Position in Firebase (same structure as app)
|
|
291
|
+
*/
|
|
292
|
+
interface FirebasePosition {
|
|
293
|
+
x: number;
|
|
294
|
+
y: number;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Seat data stored in Firebase
|
|
298
|
+
* Stored at: seatmaps/{seatMapId}/seats/{seatId}
|
|
299
|
+
*/
|
|
300
|
+
interface FirebaseSeat {
|
|
301
|
+
position: FirebasePosition;
|
|
302
|
+
shape: SeatShape;
|
|
303
|
+
state: FirebaseSeatState;
|
|
304
|
+
sectionName?: string;
|
|
305
|
+
rowLabel?: string;
|
|
306
|
+
columnLabel?: string;
|
|
307
|
+
seatNumber?: string;
|
|
308
|
+
price?: number;
|
|
309
|
+
floorId?: string;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Stage configuration in Firebase
|
|
313
|
+
* Stored at: seatmaps/{seatMapId}/config/stages/{stageId}
|
|
314
|
+
*/
|
|
315
|
+
interface FirebaseStage {
|
|
316
|
+
position: FirebasePosition;
|
|
317
|
+
config: {
|
|
318
|
+
label: string;
|
|
319
|
+
width: number;
|
|
320
|
+
height: number;
|
|
321
|
+
rotation?: number;
|
|
322
|
+
color?: string;
|
|
323
|
+
objectType?: ObjectType;
|
|
324
|
+
};
|
|
325
|
+
floorId?: string;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Canvas configuration in Firebase
|
|
329
|
+
* Stored at: seatmaps/{seatMapId}/config/canvas
|
|
330
|
+
*/
|
|
331
|
+
interface FirebaseCanvasConfig {
|
|
332
|
+
width: number;
|
|
333
|
+
height: number;
|
|
334
|
+
backgroundColor: string;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Seat map metadata in Firebase
|
|
338
|
+
* Stored at: seatmaps/{seatMapId}/meta
|
|
339
|
+
*/
|
|
340
|
+
interface FirebaseSeatMapMeta {
|
|
341
|
+
event_id: number;
|
|
342
|
+
sub_event_id: number;
|
|
343
|
+
name: string;
|
|
344
|
+
venue?: string;
|
|
345
|
+
capacity?: number;
|
|
346
|
+
updated_at: number;
|
|
347
|
+
version: string;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Design configuration in Firebase (excludes seats which are separate)
|
|
351
|
+
* Stored at: seatmaps/{seatMapId}/config
|
|
352
|
+
*/
|
|
353
|
+
interface FirebaseSeatMapConfig {
|
|
354
|
+
canvas: FirebaseCanvasConfig;
|
|
355
|
+
colors: ColorSettings;
|
|
356
|
+
floors?: Record<string, FloorConfig>;
|
|
357
|
+
stages?: Record<string, FirebaseStage>;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Complete seat map data structure in Firebase
|
|
361
|
+
* Stored at: seatmaps/{seatMapId}
|
|
362
|
+
*/
|
|
363
|
+
interface FirebaseSeatMap {
|
|
364
|
+
meta: FirebaseSeatMapMeta;
|
|
365
|
+
config: FirebaseSeatMapConfig;
|
|
366
|
+
seats: Record<string, FirebaseSeat>;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Index entry for seat map lookup by event
|
|
370
|
+
* Stored at: indexes/by_event/{eventId}/{seatMapId}
|
|
371
|
+
*/
|
|
372
|
+
type FirebaseIndexEntry = true;
|
|
373
|
+
/**
|
|
374
|
+
* Options for Firebase real-time hooks
|
|
375
|
+
*/
|
|
376
|
+
interface FirebaseHookOptions {
|
|
377
|
+
enabled?: boolean;
|
|
378
|
+
onError?: (error: Error) => void;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Result from Firebase seat states hook
|
|
382
|
+
*/
|
|
383
|
+
interface FirebaseSeatStatesResult {
|
|
384
|
+
states: FirebaseSeatStates | null;
|
|
385
|
+
loading: boolean;
|
|
386
|
+
error: Error | null;
|
|
387
|
+
lastUpdated: number | null;
|
|
388
|
+
myReservedSeats: string[];
|
|
389
|
+
otherReservedSeats: string[];
|
|
390
|
+
unavailableSeats: string[];
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Result from Firebase config hook
|
|
394
|
+
*/
|
|
395
|
+
interface FirebaseConfigResult {
|
|
396
|
+
config: FirebaseSeatMap | null;
|
|
397
|
+
loading: boolean;
|
|
398
|
+
error: Error | null;
|
|
399
|
+
refetch: () => Promise<void>;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Firebase Realtime Database path utilities
|
|
404
|
+
* Provides type-safe path builders for all Firebase nodes
|
|
405
|
+
*/
|
|
406
|
+
/**
|
|
407
|
+
* All Firebase database paths used by Seat Map Studio
|
|
408
|
+
*/
|
|
409
|
+
declare const FirebasePaths: {
|
|
410
|
+
readonly seatmap: (seatMapId: string) => `seatmaps/${string}`;
|
|
411
|
+
readonly seatmapMeta: (seatMapId: string) => `seatmaps/${string}/meta`;
|
|
412
|
+
readonly seatmapConfig: (seatMapId: string) => `seatmaps/${string}/config`;
|
|
413
|
+
readonly seatmapSeats: (seatMapId: string) => `seatmaps/${string}/seats`;
|
|
414
|
+
readonly seatmapSeat: (seatMapId: string, seatId: string) => `seatmaps/${string}/seats/${string}`;
|
|
415
|
+
readonly seatStates: (seatMapId: string) => `seat_states/${string}`;
|
|
416
|
+
readonly seatState: (seatMapId: string, seatId: string) => `seat_states/${string}/${string}`;
|
|
417
|
+
readonly indexByEvent: (eventId: number) => `indexes/by_event/${number}`;
|
|
418
|
+
readonly indexBySubEvent: (subEventId: number) => `indexes/by_sub_event/${number}`;
|
|
419
|
+
readonly indexEntry: (eventId: number, seatMapId: string) => `indexes/by_event/${number}/${string}`;
|
|
420
|
+
readonly indexSubEventEntry: (subEventId: number, seatMapId: string) => `indexes/by_sub_event/${number}/${string}`;
|
|
421
|
+
};
|
|
422
|
+
/**
|
|
423
|
+
* Encode seat ID for Firebase path (Firebase doesn't allow certain characters)
|
|
424
|
+
* Replaces: . # $ [ ] /
|
|
425
|
+
*/
|
|
426
|
+
declare function encodeSeatId(seatId: string): string;
|
|
427
|
+
/**
|
|
428
|
+
* Decode seat ID from Firebase path
|
|
429
|
+
*/
|
|
430
|
+
declare function decodeSeatId(encodedId: string): string;
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Converters between SeatMapConfig and Firebase data structures
|
|
434
|
+
*/
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Convert app SeatState to Firebase state (filters out 'selected' and 'hidden')
|
|
438
|
+
*/
|
|
439
|
+
declare function toFirebaseState(state: SeatState): FirebaseSeatState;
|
|
440
|
+
/**
|
|
441
|
+
* Convert Firebase state to app SeatState
|
|
442
|
+
*/
|
|
443
|
+
declare function fromFirebaseState(state: FirebaseSeatState): SeatState;
|
|
444
|
+
/**
|
|
445
|
+
* Convert SeatMapConfig to Firebase format
|
|
446
|
+
*/
|
|
447
|
+
declare function toFirebaseSeatMap(config: SeatMapConfig, eventId: number, subEventId: number): FirebaseSeatMap;
|
|
448
|
+
/**
|
|
449
|
+
* Convert Firebase data to SeatMapConfig
|
|
450
|
+
*/
|
|
451
|
+
declare function fromFirebaseSeatMap(firebase: FirebaseSeatMap, seatStates?: FirebaseSeatStates): SeatMapConfig;
|
|
452
|
+
/**
|
|
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)
|
|
458
|
+
*/
|
|
459
|
+
declare function extractSeatStates(config: SeatMapConfig): FirebaseSeatStates;
|
|
460
|
+
/**
|
|
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
|
|
466
|
+
*/
|
|
467
|
+
declare function deriveSeatArraysFromStates(states: FirebaseSeatStates, currentUserId?: string): {
|
|
468
|
+
myReservedSeats: string[];
|
|
469
|
+
otherReservedSeats: string[];
|
|
470
|
+
unavailableSeats: string[];
|
|
471
|
+
reservedSeats: string[];
|
|
472
|
+
};
|
|
473
|
+
/**
|
|
474
|
+
* Create index updates for a seat map
|
|
475
|
+
*/
|
|
476
|
+
declare function createIndexUpdates(seatMapId: string, eventId: number, subEventId: number): Record<string, boolean>;
|
|
477
|
+
|
|
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
|
@@ -254,4 +254,225 @@ declare function importConfigFromJSON(jsonString: string): SeatMapConfig;
|
|
|
254
254
|
*/
|
|
255
255
|
declare function downloadConfigAsFile(config: SeatMapConfig, filename?: string): void;
|
|
256
256
|
|
|
257
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Firebase Realtime Database type definitions for Seat Map Studio
|
|
259
|
+
*/
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Firebase seat state type (excludes 'selected' which is client-only, 'hidden' which is filtered)
|
|
263
|
+
*/
|
|
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;
|
|
279
|
+
/**
|
|
280
|
+
* Lightweight seat states map for high-frequency real-time updates
|
|
281
|
+
* Stored at: seat_states/{seatMapId}
|
|
282
|
+
*
|
|
283
|
+
* Keys: encoded seat IDs
|
|
284
|
+
* Values: null = available, object = reserved/unavailable with user info
|
|
285
|
+
*/
|
|
286
|
+
interface FirebaseSeatStates {
|
|
287
|
+
[seatId: string]: FirebaseSeatStateEntry;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Position in Firebase (same structure as app)
|
|
291
|
+
*/
|
|
292
|
+
interface FirebasePosition {
|
|
293
|
+
x: number;
|
|
294
|
+
y: number;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Seat data stored in Firebase
|
|
298
|
+
* Stored at: seatmaps/{seatMapId}/seats/{seatId}
|
|
299
|
+
*/
|
|
300
|
+
interface FirebaseSeat {
|
|
301
|
+
position: FirebasePosition;
|
|
302
|
+
shape: SeatShape;
|
|
303
|
+
state: FirebaseSeatState;
|
|
304
|
+
sectionName?: string;
|
|
305
|
+
rowLabel?: string;
|
|
306
|
+
columnLabel?: string;
|
|
307
|
+
seatNumber?: string;
|
|
308
|
+
price?: number;
|
|
309
|
+
floorId?: string;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Stage configuration in Firebase
|
|
313
|
+
* Stored at: seatmaps/{seatMapId}/config/stages/{stageId}
|
|
314
|
+
*/
|
|
315
|
+
interface FirebaseStage {
|
|
316
|
+
position: FirebasePosition;
|
|
317
|
+
config: {
|
|
318
|
+
label: string;
|
|
319
|
+
width: number;
|
|
320
|
+
height: number;
|
|
321
|
+
rotation?: number;
|
|
322
|
+
color?: string;
|
|
323
|
+
objectType?: ObjectType;
|
|
324
|
+
};
|
|
325
|
+
floorId?: string;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Canvas configuration in Firebase
|
|
329
|
+
* Stored at: seatmaps/{seatMapId}/config/canvas
|
|
330
|
+
*/
|
|
331
|
+
interface FirebaseCanvasConfig {
|
|
332
|
+
width: number;
|
|
333
|
+
height: number;
|
|
334
|
+
backgroundColor: string;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Seat map metadata in Firebase
|
|
338
|
+
* Stored at: seatmaps/{seatMapId}/meta
|
|
339
|
+
*/
|
|
340
|
+
interface FirebaseSeatMapMeta {
|
|
341
|
+
event_id: number;
|
|
342
|
+
sub_event_id: number;
|
|
343
|
+
name: string;
|
|
344
|
+
venue?: string;
|
|
345
|
+
capacity?: number;
|
|
346
|
+
updated_at: number;
|
|
347
|
+
version: string;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Design configuration in Firebase (excludes seats which are separate)
|
|
351
|
+
* Stored at: seatmaps/{seatMapId}/config
|
|
352
|
+
*/
|
|
353
|
+
interface FirebaseSeatMapConfig {
|
|
354
|
+
canvas: FirebaseCanvasConfig;
|
|
355
|
+
colors: ColorSettings;
|
|
356
|
+
floors?: Record<string, FloorConfig>;
|
|
357
|
+
stages?: Record<string, FirebaseStage>;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Complete seat map data structure in Firebase
|
|
361
|
+
* Stored at: seatmaps/{seatMapId}
|
|
362
|
+
*/
|
|
363
|
+
interface FirebaseSeatMap {
|
|
364
|
+
meta: FirebaseSeatMapMeta;
|
|
365
|
+
config: FirebaseSeatMapConfig;
|
|
366
|
+
seats: Record<string, FirebaseSeat>;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Index entry for seat map lookup by event
|
|
370
|
+
* Stored at: indexes/by_event/{eventId}/{seatMapId}
|
|
371
|
+
*/
|
|
372
|
+
type FirebaseIndexEntry = true;
|
|
373
|
+
/**
|
|
374
|
+
* Options for Firebase real-time hooks
|
|
375
|
+
*/
|
|
376
|
+
interface FirebaseHookOptions {
|
|
377
|
+
enabled?: boolean;
|
|
378
|
+
onError?: (error: Error) => void;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Result from Firebase seat states hook
|
|
382
|
+
*/
|
|
383
|
+
interface FirebaseSeatStatesResult {
|
|
384
|
+
states: FirebaseSeatStates | null;
|
|
385
|
+
loading: boolean;
|
|
386
|
+
error: Error | null;
|
|
387
|
+
lastUpdated: number | null;
|
|
388
|
+
myReservedSeats: string[];
|
|
389
|
+
otherReservedSeats: string[];
|
|
390
|
+
unavailableSeats: string[];
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Result from Firebase config hook
|
|
394
|
+
*/
|
|
395
|
+
interface FirebaseConfigResult {
|
|
396
|
+
config: FirebaseSeatMap | null;
|
|
397
|
+
loading: boolean;
|
|
398
|
+
error: Error | null;
|
|
399
|
+
refetch: () => Promise<void>;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Firebase Realtime Database path utilities
|
|
404
|
+
* Provides type-safe path builders for all Firebase nodes
|
|
405
|
+
*/
|
|
406
|
+
/**
|
|
407
|
+
* All Firebase database paths used by Seat Map Studio
|
|
408
|
+
*/
|
|
409
|
+
declare const FirebasePaths: {
|
|
410
|
+
readonly seatmap: (seatMapId: string) => `seatmaps/${string}`;
|
|
411
|
+
readonly seatmapMeta: (seatMapId: string) => `seatmaps/${string}/meta`;
|
|
412
|
+
readonly seatmapConfig: (seatMapId: string) => `seatmaps/${string}/config`;
|
|
413
|
+
readonly seatmapSeats: (seatMapId: string) => `seatmaps/${string}/seats`;
|
|
414
|
+
readonly seatmapSeat: (seatMapId: string, seatId: string) => `seatmaps/${string}/seats/${string}`;
|
|
415
|
+
readonly seatStates: (seatMapId: string) => `seat_states/${string}`;
|
|
416
|
+
readonly seatState: (seatMapId: string, seatId: string) => `seat_states/${string}/${string}`;
|
|
417
|
+
readonly indexByEvent: (eventId: number) => `indexes/by_event/${number}`;
|
|
418
|
+
readonly indexBySubEvent: (subEventId: number) => `indexes/by_sub_event/${number}`;
|
|
419
|
+
readonly indexEntry: (eventId: number, seatMapId: string) => `indexes/by_event/${number}/${string}`;
|
|
420
|
+
readonly indexSubEventEntry: (subEventId: number, seatMapId: string) => `indexes/by_sub_event/${number}/${string}`;
|
|
421
|
+
};
|
|
422
|
+
/**
|
|
423
|
+
* Encode seat ID for Firebase path (Firebase doesn't allow certain characters)
|
|
424
|
+
* Replaces: . # $ [ ] /
|
|
425
|
+
*/
|
|
426
|
+
declare function encodeSeatId(seatId: string): string;
|
|
427
|
+
/**
|
|
428
|
+
* Decode seat ID from Firebase path
|
|
429
|
+
*/
|
|
430
|
+
declare function decodeSeatId(encodedId: string): string;
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Converters between SeatMapConfig and Firebase data structures
|
|
434
|
+
*/
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Convert app SeatState to Firebase state (filters out 'selected' and 'hidden')
|
|
438
|
+
*/
|
|
439
|
+
declare function toFirebaseState(state: SeatState): FirebaseSeatState;
|
|
440
|
+
/**
|
|
441
|
+
* Convert Firebase state to app SeatState
|
|
442
|
+
*/
|
|
443
|
+
declare function fromFirebaseState(state: FirebaseSeatState): SeatState;
|
|
444
|
+
/**
|
|
445
|
+
* Convert SeatMapConfig to Firebase format
|
|
446
|
+
*/
|
|
447
|
+
declare function toFirebaseSeatMap(config: SeatMapConfig, eventId: number, subEventId: number): FirebaseSeatMap;
|
|
448
|
+
/**
|
|
449
|
+
* Convert Firebase data to SeatMapConfig
|
|
450
|
+
*/
|
|
451
|
+
declare function fromFirebaseSeatMap(firebase: FirebaseSeatMap, seatStates?: FirebaseSeatStates): SeatMapConfig;
|
|
452
|
+
/**
|
|
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)
|
|
458
|
+
*/
|
|
459
|
+
declare function extractSeatStates(config: SeatMapConfig): FirebaseSeatStates;
|
|
460
|
+
/**
|
|
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
|
|
466
|
+
*/
|
|
467
|
+
declare function deriveSeatArraysFromStates(states: FirebaseSeatStates, currentUserId?: string): {
|
|
468
|
+
myReservedSeats: string[];
|
|
469
|
+
otherReservedSeats: string[];
|
|
470
|
+
unavailableSeats: string[];
|
|
471
|
+
reservedSeats: string[];
|
|
472
|
+
};
|
|
473
|
+
/**
|
|
474
|
+
* Create index updates for a seat map
|
|
475
|
+
*/
|
|
476
|
+
declare function createIndexUpdates(seatMapId: string, eventId: number, subEventId: number): Record<string, boolean>;
|
|
477
|
+
|
|
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
|
@@ -21,18 +21,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
DEFAULT_COLORS: () => DEFAULT_COLORS,
|
|
24
|
+
FirebasePaths: () => FirebasePaths,
|
|
24
25
|
applySeatStateOverrides: () => applySeatStateOverrides,
|
|
25
26
|
calculateAvailableSeats: () => calculateAvailableSeats,
|
|
26
27
|
calculateCapacity: () => calculateCapacity,
|
|
27
28
|
calculateSeatPrice: () => calculateSeatPrice,
|
|
28
29
|
cloneConfig: () => cloneConfig,
|
|
29
30
|
createDefaultConfig: () => createDefaultConfig,
|
|
31
|
+
createIndexUpdates: () => createIndexUpdates,
|
|
32
|
+
decodeSeatId: () => decodeSeatId,
|
|
33
|
+
deriveSeatArraysFromStates: () => deriveSeatArraysFromStates,
|
|
30
34
|
downloadConfigAsFile: () => downloadConfigAsFile,
|
|
35
|
+
encodeSeatId: () => encodeSeatId,
|
|
31
36
|
exportConfigAsJSON: () => exportConfigAsJSON,
|
|
37
|
+
extractSeatStates: () => extractSeatStates,
|
|
32
38
|
formatDate: () => formatDate,
|
|
39
|
+
fromFirebaseSeatMap: () => fromFirebaseSeatMap,
|
|
40
|
+
fromFirebaseState: () => fromFirebaseState,
|
|
33
41
|
generateId: () => generateId,
|
|
34
42
|
getSelectedSeats: () => getSelectedSeats,
|
|
35
43
|
importConfigFromJSON: () => importConfigFromJSON,
|
|
44
|
+
toFirebaseSeatMap: () => toFirebaseSeatMap,
|
|
45
|
+
toFirebaseState: () => toFirebaseState,
|
|
36
46
|
updateConfigTimestamp: () => updateConfigTimestamp,
|
|
37
47
|
validateSeatMapConfig: () => validateSeatMapConfig
|
|
38
48
|
});
|
|
@@ -262,21 +272,242 @@ function downloadConfigAsFile(config, filename = "seat-map-config.json") {
|
|
|
262
272
|
document.body.removeChild(link);
|
|
263
273
|
URL.revokeObjectURL(url);
|
|
264
274
|
}
|
|
275
|
+
|
|
276
|
+
// src/firebase/schema.ts
|
|
277
|
+
var FirebasePaths = {
|
|
278
|
+
// Seat map nodes
|
|
279
|
+
seatmap: (seatMapId) => `seatmaps/${seatMapId}`,
|
|
280
|
+
seatmapMeta: (seatMapId) => `seatmaps/${seatMapId}/meta`,
|
|
281
|
+
seatmapConfig: (seatMapId) => `seatmaps/${seatMapId}/config`,
|
|
282
|
+
seatmapSeats: (seatMapId) => `seatmaps/${seatMapId}/seats`,
|
|
283
|
+
seatmapSeat: (seatMapId, seatId) => `seatmaps/${seatMapId}/seats/${seatId}`,
|
|
284
|
+
// Lightweight seat states node (for high-frequency updates)
|
|
285
|
+
seatStates: (seatMapId) => `seat_states/${seatMapId}`,
|
|
286
|
+
seatState: (seatMapId, seatId) => `seat_states/${seatMapId}/${seatId}`,
|
|
287
|
+
// Index nodes for efficient lookups
|
|
288
|
+
indexByEvent: (eventId) => `indexes/by_event/${eventId}`,
|
|
289
|
+
indexBySubEvent: (subEventId) => `indexes/by_sub_event/${subEventId}`,
|
|
290
|
+
indexEntry: (eventId, seatMapId) => `indexes/by_event/${eventId}/${seatMapId}`,
|
|
291
|
+
indexSubEventEntry: (subEventId, seatMapId) => `indexes/by_sub_event/${subEventId}/${seatMapId}`
|
|
292
|
+
};
|
|
293
|
+
function encodeSeatId(seatId) {
|
|
294
|
+
return seatId.replace(/\./g, "%2E").replace(/#/g, "%23").replace(/\$/g, "%24").replace(/\[/g, "%5B").replace(/\]/g, "%5D").replace(/\//g, "%2F");
|
|
295
|
+
}
|
|
296
|
+
function decodeSeatId(encodedId) {
|
|
297
|
+
return encodedId.replace(/%2E/g, ".").replace(/%23/g, "#").replace(/%24/g, "$").replace(/%5B/g, "[").replace(/%5D/g, "]").replace(/%2F/g, "/");
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/firebase/converters.ts
|
|
301
|
+
function removeUndefined(obj) {
|
|
302
|
+
if (typeof obj !== "object" || obj === null) return obj;
|
|
303
|
+
const result = {};
|
|
304
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
305
|
+
if (value !== void 0) {
|
|
306
|
+
result[key] = value;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
function toFirebaseState(state) {
|
|
312
|
+
if (state === "selected") return "available";
|
|
313
|
+
if (state === "hidden") return "unavailable";
|
|
314
|
+
return state;
|
|
315
|
+
}
|
|
316
|
+
function fromFirebaseState(state) {
|
|
317
|
+
return state;
|
|
318
|
+
}
|
|
319
|
+
function toFirebaseSeatMap(config, eventId, subEventId) {
|
|
320
|
+
const meta = removeUndefined({
|
|
321
|
+
event_id: eventId,
|
|
322
|
+
sub_event_id: subEventId,
|
|
323
|
+
name: config.metadata.name,
|
|
324
|
+
venue: config.metadata.venue,
|
|
325
|
+
capacity: config.metadata.capacity,
|
|
326
|
+
updated_at: Date.now(),
|
|
327
|
+
version: config.version
|
|
328
|
+
});
|
|
329
|
+
const seats = {};
|
|
330
|
+
for (const seat of config.seats) {
|
|
331
|
+
const encodedId = encodeSeatId(seat.id);
|
|
332
|
+
seats[encodedId] = removeUndefined({
|
|
333
|
+
position: seat.position,
|
|
334
|
+
shape: seat.shape,
|
|
335
|
+
state: toFirebaseState(seat.state),
|
|
336
|
+
sectionName: seat.sectionName,
|
|
337
|
+
rowLabel: seat.rowLabel,
|
|
338
|
+
columnLabel: seat.columnLabel,
|
|
339
|
+
seatNumber: seat.seatNumber,
|
|
340
|
+
price: seat.price,
|
|
341
|
+
floorId: seat.floorId
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
let stages = null;
|
|
345
|
+
if (config.stages && config.stages.length > 0) {
|
|
346
|
+
stages = {};
|
|
347
|
+
for (const stage of config.stages) {
|
|
348
|
+
stages[stage.id] = removeUndefined({
|
|
349
|
+
position: stage.position,
|
|
350
|
+
config: removeUndefined({
|
|
351
|
+
label: stage.config.label,
|
|
352
|
+
width: stage.config.width,
|
|
353
|
+
height: stage.config.height,
|
|
354
|
+
rotation: stage.config.rotation,
|
|
355
|
+
color: stage.config.color
|
|
356
|
+
}),
|
|
357
|
+
floorId: stage.floorId
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
let floors = null;
|
|
362
|
+
if (config.floors && config.floors.length > 0) {
|
|
363
|
+
floors = {};
|
|
364
|
+
for (const floor of config.floors) {
|
|
365
|
+
floors[floor.id] = {
|
|
366
|
+
id: floor.id,
|
|
367
|
+
name: floor.name,
|
|
368
|
+
order: floor.order,
|
|
369
|
+
...floor.color !== void 0 ? { color: floor.color } : {}
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const firebaseConfig = {
|
|
374
|
+
canvas: config.canvas,
|
|
375
|
+
colors: config.colors
|
|
376
|
+
};
|
|
377
|
+
if (floors) {
|
|
378
|
+
firebaseConfig.floors = floors;
|
|
379
|
+
}
|
|
380
|
+
if (stages) {
|
|
381
|
+
firebaseConfig.stages = stages;
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
meta,
|
|
385
|
+
config: firebaseConfig,
|
|
386
|
+
seats
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
function fromFirebaseSeatMap(firebase, seatStates) {
|
|
390
|
+
const seats = Object.entries(firebase.seats || {}).map(
|
|
391
|
+
([encodedId, seat]) => {
|
|
392
|
+
const id = decodeSeatId(encodedId);
|
|
393
|
+
let effectiveState = seat.state;
|
|
394
|
+
const stateEntry = seatStates?.[encodedId] ?? seatStates?.[id];
|
|
395
|
+
if (stateEntry) {
|
|
396
|
+
effectiveState = stateEntry.state;
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
id,
|
|
400
|
+
position: seat.position,
|
|
401
|
+
shape: seat.shape,
|
|
402
|
+
state: fromFirebaseState(effectiveState),
|
|
403
|
+
sectionName: seat.sectionName,
|
|
404
|
+
rowLabel: seat.rowLabel,
|
|
405
|
+
columnLabel: seat.columnLabel,
|
|
406
|
+
seatNumber: seat.seatNumber,
|
|
407
|
+
price: seat.price,
|
|
408
|
+
floorId: seat.floorId
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
);
|
|
412
|
+
const stages = firebase.config.stages ? Object.entries(firebase.config.stages).map(([id, stage]) => ({
|
|
413
|
+
id,
|
|
414
|
+
position: stage.position,
|
|
415
|
+
config: {
|
|
416
|
+
label: stage.config.label,
|
|
417
|
+
width: stage.config.width,
|
|
418
|
+
height: stage.config.height,
|
|
419
|
+
rotation: stage.config.rotation,
|
|
420
|
+
color: stage.config.color
|
|
421
|
+
},
|
|
422
|
+
floorId: stage.floorId
|
|
423
|
+
})) : void 0;
|
|
424
|
+
const floors = firebase.config.floors ? Object.values(firebase.config.floors).sort((a, b) => a.order - b.order) : void 0;
|
|
425
|
+
return {
|
|
426
|
+
version: firebase.meta.version,
|
|
427
|
+
metadata: {
|
|
428
|
+
name: firebase.meta.name,
|
|
429
|
+
venue: firebase.meta.venue,
|
|
430
|
+
capacity: firebase.meta.capacity,
|
|
431
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
432
|
+
// Not stored in Firebase
|
|
433
|
+
updatedAt: new Date(firebase.meta.updated_at).toISOString()
|
|
434
|
+
},
|
|
435
|
+
canvas: firebase.config.canvas,
|
|
436
|
+
colors: firebase.config.colors,
|
|
437
|
+
seats,
|
|
438
|
+
stages,
|
|
439
|
+
floors
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function extractSeatStates(config) {
|
|
443
|
+
const states = {};
|
|
444
|
+
for (const seat of config.seats) {
|
|
445
|
+
if (seat.state === "hidden") continue;
|
|
446
|
+
if (seat.state === "available" || seat.state === "selected") continue;
|
|
447
|
+
const encodedId = encodeSeatId(seat.id);
|
|
448
|
+
states[encodedId] = {
|
|
449
|
+
state: seat.state,
|
|
450
|
+
timestamp: Date.now()
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return states;
|
|
454
|
+
}
|
|
455
|
+
function deriveSeatArraysFromStates(states, currentUserId) {
|
|
456
|
+
const myReservedSeats = [];
|
|
457
|
+
const otherReservedSeats = [];
|
|
458
|
+
const unavailableSeats = [];
|
|
459
|
+
for (const [encodedId, entry] of Object.entries(states)) {
|
|
460
|
+
if (!entry) continue;
|
|
461
|
+
const id = decodeSeatId(encodedId);
|
|
462
|
+
if (entry.state === "unavailable") {
|
|
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
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return {
|
|
473
|
+
myReservedSeats,
|
|
474
|
+
otherReservedSeats,
|
|
475
|
+
unavailableSeats,
|
|
476
|
+
// Legacy: all reserved seats (for non-user-aware usage)
|
|
477
|
+
reservedSeats: [...myReservedSeats, ...otherReservedSeats]
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function createIndexUpdates(seatMapId, eventId, subEventId) {
|
|
481
|
+
return {
|
|
482
|
+
[`indexes/by_event/${eventId}/${seatMapId}`]: true,
|
|
483
|
+
[`indexes/by_sub_event/${subEventId}/${seatMapId}`]: true
|
|
484
|
+
};
|
|
485
|
+
}
|
|
265
486
|
// Annotate the CommonJS export names for ESM import in node:
|
|
266
487
|
0 && (module.exports = {
|
|
267
488
|
DEFAULT_COLORS,
|
|
489
|
+
FirebasePaths,
|
|
268
490
|
applySeatStateOverrides,
|
|
269
491
|
calculateAvailableSeats,
|
|
270
492
|
calculateCapacity,
|
|
271
493
|
calculateSeatPrice,
|
|
272
494
|
cloneConfig,
|
|
273
495
|
createDefaultConfig,
|
|
496
|
+
createIndexUpdates,
|
|
497
|
+
decodeSeatId,
|
|
498
|
+
deriveSeatArraysFromStates,
|
|
274
499
|
downloadConfigAsFile,
|
|
500
|
+
encodeSeatId,
|
|
275
501
|
exportConfigAsJSON,
|
|
502
|
+
extractSeatStates,
|
|
276
503
|
formatDate,
|
|
504
|
+
fromFirebaseSeatMap,
|
|
505
|
+
fromFirebaseState,
|
|
277
506
|
generateId,
|
|
278
507
|
getSelectedSeats,
|
|
279
508
|
importConfigFromJSON,
|
|
509
|
+
toFirebaseSeatMap,
|
|
510
|
+
toFirebaseState,
|
|
280
511
|
updateConfigTimestamp,
|
|
281
512
|
validateSeatMapConfig
|
|
282
513
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -222,20 +222,241 @@ function downloadConfigAsFile(config, filename = "seat-map-config.json") {
|
|
|
222
222
|
document.body.removeChild(link);
|
|
223
223
|
URL.revokeObjectURL(url);
|
|
224
224
|
}
|
|
225
|
+
|
|
226
|
+
// src/firebase/schema.ts
|
|
227
|
+
var FirebasePaths = {
|
|
228
|
+
// Seat map nodes
|
|
229
|
+
seatmap: (seatMapId) => `seatmaps/${seatMapId}`,
|
|
230
|
+
seatmapMeta: (seatMapId) => `seatmaps/${seatMapId}/meta`,
|
|
231
|
+
seatmapConfig: (seatMapId) => `seatmaps/${seatMapId}/config`,
|
|
232
|
+
seatmapSeats: (seatMapId) => `seatmaps/${seatMapId}/seats`,
|
|
233
|
+
seatmapSeat: (seatMapId, seatId) => `seatmaps/${seatMapId}/seats/${seatId}`,
|
|
234
|
+
// Lightweight seat states node (for high-frequency updates)
|
|
235
|
+
seatStates: (seatMapId) => `seat_states/${seatMapId}`,
|
|
236
|
+
seatState: (seatMapId, seatId) => `seat_states/${seatMapId}/${seatId}`,
|
|
237
|
+
// Index nodes for efficient lookups
|
|
238
|
+
indexByEvent: (eventId) => `indexes/by_event/${eventId}`,
|
|
239
|
+
indexBySubEvent: (subEventId) => `indexes/by_sub_event/${subEventId}`,
|
|
240
|
+
indexEntry: (eventId, seatMapId) => `indexes/by_event/${eventId}/${seatMapId}`,
|
|
241
|
+
indexSubEventEntry: (subEventId, seatMapId) => `indexes/by_sub_event/${subEventId}/${seatMapId}`
|
|
242
|
+
};
|
|
243
|
+
function encodeSeatId(seatId) {
|
|
244
|
+
return seatId.replace(/\./g, "%2E").replace(/#/g, "%23").replace(/\$/g, "%24").replace(/\[/g, "%5B").replace(/\]/g, "%5D").replace(/\//g, "%2F");
|
|
245
|
+
}
|
|
246
|
+
function decodeSeatId(encodedId) {
|
|
247
|
+
return encodedId.replace(/%2E/g, ".").replace(/%23/g, "#").replace(/%24/g, "$").replace(/%5B/g, "[").replace(/%5D/g, "]").replace(/%2F/g, "/");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/firebase/converters.ts
|
|
251
|
+
function removeUndefined(obj) {
|
|
252
|
+
if (typeof obj !== "object" || obj === null) return obj;
|
|
253
|
+
const result = {};
|
|
254
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
255
|
+
if (value !== void 0) {
|
|
256
|
+
result[key] = value;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
function toFirebaseState(state) {
|
|
262
|
+
if (state === "selected") return "available";
|
|
263
|
+
if (state === "hidden") return "unavailable";
|
|
264
|
+
return state;
|
|
265
|
+
}
|
|
266
|
+
function fromFirebaseState(state) {
|
|
267
|
+
return state;
|
|
268
|
+
}
|
|
269
|
+
function toFirebaseSeatMap(config, eventId, subEventId) {
|
|
270
|
+
const meta = removeUndefined({
|
|
271
|
+
event_id: eventId,
|
|
272
|
+
sub_event_id: subEventId,
|
|
273
|
+
name: config.metadata.name,
|
|
274
|
+
venue: config.metadata.venue,
|
|
275
|
+
capacity: config.metadata.capacity,
|
|
276
|
+
updated_at: Date.now(),
|
|
277
|
+
version: config.version
|
|
278
|
+
});
|
|
279
|
+
const seats = {};
|
|
280
|
+
for (const seat of config.seats) {
|
|
281
|
+
const encodedId = encodeSeatId(seat.id);
|
|
282
|
+
seats[encodedId] = removeUndefined({
|
|
283
|
+
position: seat.position,
|
|
284
|
+
shape: seat.shape,
|
|
285
|
+
state: toFirebaseState(seat.state),
|
|
286
|
+
sectionName: seat.sectionName,
|
|
287
|
+
rowLabel: seat.rowLabel,
|
|
288
|
+
columnLabel: seat.columnLabel,
|
|
289
|
+
seatNumber: seat.seatNumber,
|
|
290
|
+
price: seat.price,
|
|
291
|
+
floorId: seat.floorId
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
let stages = null;
|
|
295
|
+
if (config.stages && config.stages.length > 0) {
|
|
296
|
+
stages = {};
|
|
297
|
+
for (const stage of config.stages) {
|
|
298
|
+
stages[stage.id] = removeUndefined({
|
|
299
|
+
position: stage.position,
|
|
300
|
+
config: removeUndefined({
|
|
301
|
+
label: stage.config.label,
|
|
302
|
+
width: stage.config.width,
|
|
303
|
+
height: stage.config.height,
|
|
304
|
+
rotation: stage.config.rotation,
|
|
305
|
+
color: stage.config.color
|
|
306
|
+
}),
|
|
307
|
+
floorId: stage.floorId
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
let floors = null;
|
|
312
|
+
if (config.floors && config.floors.length > 0) {
|
|
313
|
+
floors = {};
|
|
314
|
+
for (const floor of config.floors) {
|
|
315
|
+
floors[floor.id] = {
|
|
316
|
+
id: floor.id,
|
|
317
|
+
name: floor.name,
|
|
318
|
+
order: floor.order,
|
|
319
|
+
...floor.color !== void 0 ? { color: floor.color } : {}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const firebaseConfig = {
|
|
324
|
+
canvas: config.canvas,
|
|
325
|
+
colors: config.colors
|
|
326
|
+
};
|
|
327
|
+
if (floors) {
|
|
328
|
+
firebaseConfig.floors = floors;
|
|
329
|
+
}
|
|
330
|
+
if (stages) {
|
|
331
|
+
firebaseConfig.stages = stages;
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
meta,
|
|
335
|
+
config: firebaseConfig,
|
|
336
|
+
seats
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function fromFirebaseSeatMap(firebase, seatStates) {
|
|
340
|
+
const seats = Object.entries(firebase.seats || {}).map(
|
|
341
|
+
([encodedId, seat]) => {
|
|
342
|
+
const id = decodeSeatId(encodedId);
|
|
343
|
+
let effectiveState = seat.state;
|
|
344
|
+
const stateEntry = seatStates?.[encodedId] ?? seatStates?.[id];
|
|
345
|
+
if (stateEntry) {
|
|
346
|
+
effectiveState = stateEntry.state;
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
id,
|
|
350
|
+
position: seat.position,
|
|
351
|
+
shape: seat.shape,
|
|
352
|
+
state: fromFirebaseState(effectiveState),
|
|
353
|
+
sectionName: seat.sectionName,
|
|
354
|
+
rowLabel: seat.rowLabel,
|
|
355
|
+
columnLabel: seat.columnLabel,
|
|
356
|
+
seatNumber: seat.seatNumber,
|
|
357
|
+
price: seat.price,
|
|
358
|
+
floorId: seat.floorId
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
);
|
|
362
|
+
const stages = firebase.config.stages ? Object.entries(firebase.config.stages).map(([id, stage]) => ({
|
|
363
|
+
id,
|
|
364
|
+
position: stage.position,
|
|
365
|
+
config: {
|
|
366
|
+
label: stage.config.label,
|
|
367
|
+
width: stage.config.width,
|
|
368
|
+
height: stage.config.height,
|
|
369
|
+
rotation: stage.config.rotation,
|
|
370
|
+
color: stage.config.color
|
|
371
|
+
},
|
|
372
|
+
floorId: stage.floorId
|
|
373
|
+
})) : void 0;
|
|
374
|
+
const floors = firebase.config.floors ? Object.values(firebase.config.floors).sort((a, b) => a.order - b.order) : void 0;
|
|
375
|
+
return {
|
|
376
|
+
version: firebase.meta.version,
|
|
377
|
+
metadata: {
|
|
378
|
+
name: firebase.meta.name,
|
|
379
|
+
venue: firebase.meta.venue,
|
|
380
|
+
capacity: firebase.meta.capacity,
|
|
381
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
382
|
+
// Not stored in Firebase
|
|
383
|
+
updatedAt: new Date(firebase.meta.updated_at).toISOString()
|
|
384
|
+
},
|
|
385
|
+
canvas: firebase.config.canvas,
|
|
386
|
+
colors: firebase.config.colors,
|
|
387
|
+
seats,
|
|
388
|
+
stages,
|
|
389
|
+
floors
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function extractSeatStates(config) {
|
|
393
|
+
const states = {};
|
|
394
|
+
for (const seat of config.seats) {
|
|
395
|
+
if (seat.state === "hidden") continue;
|
|
396
|
+
if (seat.state === "available" || seat.state === "selected") continue;
|
|
397
|
+
const encodedId = encodeSeatId(seat.id);
|
|
398
|
+
states[encodedId] = {
|
|
399
|
+
state: seat.state,
|
|
400
|
+
timestamp: Date.now()
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
return states;
|
|
404
|
+
}
|
|
405
|
+
function deriveSeatArraysFromStates(states, currentUserId) {
|
|
406
|
+
const myReservedSeats = [];
|
|
407
|
+
const otherReservedSeats = [];
|
|
408
|
+
const unavailableSeats = [];
|
|
409
|
+
for (const [encodedId, entry] of Object.entries(states)) {
|
|
410
|
+
if (!entry) continue;
|
|
411
|
+
const id = decodeSeatId(encodedId);
|
|
412
|
+
if (entry.state === "unavailable") {
|
|
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
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
myReservedSeats,
|
|
424
|
+
otherReservedSeats,
|
|
425
|
+
unavailableSeats,
|
|
426
|
+
// Legacy: all reserved seats (for non-user-aware usage)
|
|
427
|
+
reservedSeats: [...myReservedSeats, ...otherReservedSeats]
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function createIndexUpdates(seatMapId, eventId, subEventId) {
|
|
431
|
+
return {
|
|
432
|
+
[`indexes/by_event/${eventId}/${seatMapId}`]: true,
|
|
433
|
+
[`indexes/by_sub_event/${subEventId}/${seatMapId}`]: true
|
|
434
|
+
};
|
|
435
|
+
}
|
|
225
436
|
export {
|
|
226
437
|
DEFAULT_COLORS,
|
|
438
|
+
FirebasePaths,
|
|
227
439
|
applySeatStateOverrides,
|
|
228
440
|
calculateAvailableSeats,
|
|
229
441
|
calculateCapacity,
|
|
230
442
|
calculateSeatPrice,
|
|
231
443
|
cloneConfig,
|
|
232
444
|
createDefaultConfig,
|
|
445
|
+
createIndexUpdates,
|
|
446
|
+
decodeSeatId,
|
|
447
|
+
deriveSeatArraysFromStates,
|
|
233
448
|
downloadConfigAsFile,
|
|
449
|
+
encodeSeatId,
|
|
234
450
|
exportConfigAsJSON,
|
|
451
|
+
extractSeatStates,
|
|
235
452
|
formatDate,
|
|
453
|
+
fromFirebaseSeatMap,
|
|
454
|
+
fromFirebaseState,
|
|
236
455
|
generateId,
|
|
237
456
|
getSelectedSeats,
|
|
238
457
|
importConfigFromJSON,
|
|
458
|
+
toFirebaseSeatMap,
|
|
459
|
+
toFirebaseState,
|
|
239
460
|
updateConfigTimestamp,
|
|
240
461
|
validateSeatMapConfig
|
|
241
462
|
};
|