iticket-seatingplan-dev 2.0.8 → 2.0.18

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.
@@ -191,7 +191,7 @@ function Controls(_ref) {
191
191
  className: "leaflet-control-zoom-in single-button"
192
192
  }, /*#__PURE__*/_react.default.createElement("span", null)))), /*#__PURE__*/_react.default.createElement(_reactLeaflet.FeatureGroup, {
193
193
  ref: drawLayersRef
194
- }, /*#__PURE__*/_react.default.createElement(_reactLeafletDraw.EditControl, {
194
+ }, drawLayersRef.current && /*#__PURE__*/_react.default.createElement(_reactLeafletDraw.EditControl, {
195
195
  position: "bottomleft",
196
196
  onCreated: handleDrawBox,
197
197
  onDrawStart: () => {
@@ -210,7 +210,8 @@ function Controls(_ref) {
210
210
  },
211
211
  edit: {
212
212
  edit: false,
213
- remove: false
213
+ remove: false,
214
+ featureGroup: drawLayersRef.current
214
215
  },
215
216
  onMounted: drawInstance => {
216
217
  drawRef.current = drawInstance;
@@ -20,7 +20,8 @@ function HoverPopup(_ref) {
20
20
  show,
21
21
  availablePrices,
22
22
  prevHovered,
23
- isTouchScreen
23
+ isTouchScreen,
24
+ canUseHolds
24
25
  } = _ref;
25
26
  const map = (0, _reactLeaflet.useMap)();
26
27
  const ref = (0, _react.useRef)(null);
@@ -111,7 +112,16 @@ function HoverPopup(_ref) {
111
112
  key: "".concat(s).concat(i),
112
113
  className: "seat-name"
113
114
  }, s);
114
- })))), /*#__PURE__*/_react.default.createElement("div", {
115
+ })))), hoveredSeat !== null && hoveredSeat !== void 0 && hoveredSeat.ticketHold && canUseHolds ? /*#__PURE__*/_react.default.createElement("div", {
116
+ className: "hover-popup-ticket-hold"
117
+ }, /*#__PURE__*/_react.default.createElement("div", {
118
+ className: "held-by"
119
+ }, /*#__PURE__*/_react.default.createElement("p", null, /*#__PURE__*/_react.default.createElement("strong", null, "Held By:"), " ", hoveredSeat.ticketHold.CreatedBy), /*#__PURE__*/_react.default.createElement("p", {
120
+ className: "ticket-hold-category",
121
+ style: {
122
+ backgroundColor: "".concat(hoveredSeat.ticketHold.TicketHoldCategoryColor, "70")
123
+ }
124
+ }, hoveredSeat.ticketHold.TicketHoldCategory)), hoveredSeat.ticketHold.Note && /*#__PURE__*/_react.default.createElement("p", null, /*#__PURE__*/_react.default.createElement("strong", null, "Note:"), " ", hoveredSeat.ticketHold.Note)) : null, /*#__PURE__*/_react.default.createElement("div", {
115
125
  className: "hover-popup-price"
116
126
  }, /*#__PURE__*/_react.default.createElement("p", {
117
127
  className: "hover-popup-psname"
@@ -61,7 +61,9 @@ function SeatMap(_ref) {
61
61
  setSeatsToRemove,
62
62
  setRemoveMultipleSeatsPopupOpen,
63
63
  setInvalidSeatsPopupOpen,
64
- batchAddTicketsToCart
64
+ batchAddTicketsToCart,
65
+ canUseHolds,
66
+ bookingMode
65
67
  } = _ref;
66
68
  const [isDragging, setIsDragging] = (0, _react.useState)(false);
67
69
  const [highlightedSeats, setHighlightedSeats] = (0, _react.useState)([]);
@@ -80,6 +82,7 @@ function SeatMap(_ref) {
80
82
  const resetTooltipTimeoutRef = (0, _react.useRef)(null);
81
83
  const hideTooltipTimeoutRef = (0, _react.useRef)(null);
82
84
  const map = (0, _reactLeaflet.useMap)();
85
+ const isPOS = bookingMode === _utils.bookingModes.POS;
83
86
  const imgBounds = [[0, 0], [-_constants.SCALE, width / height * _constants.SCALE]];
84
87
  const seatsMap = new Map(seats.seats.map(s => {
85
88
  const latlng = (0, _utils.getSeatCenterLatLng)(s, height, width, imgBounds);
@@ -223,7 +226,7 @@ function SeatMap(_ref) {
223
226
  lng: seat.x
224
227
  };
225
228
  const latlng = [seatCenter.lat, seatCenter.lng];
226
- if (boxBounds.contains(latlng) && !seatsToBook.some(s => s.ssId === seat.ssId) && seat.s === _utils.statuses.UNSOLD) {
229
+ if (boxBounds.contains(latlng) && !seatsToBook.some(s => s.ssId === seat.ssId) && (seat.s === _utils.statuses.UNSOLD || seat.s === _utils.statuses.RESERVED && canUseHolds)) {
227
230
  seatsToBook.push(seat);
228
231
  }
229
232
  });
@@ -234,7 +237,7 @@ function SeatMap(_ref) {
234
237
  (_drawLayersRef$curren = drawLayersRef.current) === null || _drawLayersRef$curren === void 0 || _drawLayersRef$curren.clearLayers();
235
238
  };
236
239
  const handleHighlightSeats = (0, _react.useCallback)((e, s) => {
237
- if (s.s === _utils.statuses.UNSOLD && mode === _utils.modes.SINGLE) {
240
+ if ((s.s === _utils.statuses.UNSOLD || s.s === _utils.statuses.RESERVED && canUseHolds) && mode === _utils.modes.SINGLE) {
238
241
  const seatsToBook = (0, _utils.getValidSeats)(s, seatsMap, desiredSeatQuantity);
239
242
  if (seatsToBook.valid) {
240
243
  setHighlightedSeats(seatsToBook.seats);
@@ -296,7 +299,7 @@ function SeatMap(_ref) {
296
299
  }
297
300
  if (highlightedSeats.length === desiredSeatQuantity) {
298
301
  setClickedSeat(null);
299
- const groupedSeats = (0, _utils.groupSeatsByPriceSection)(highlightedSeats, seats.pricing, priceSectionIds);
302
+ const groupedSeats = (0, _utils.groupSeatsByPriceSection)(highlightedSeats, bookedSeats, seats.pricing, priceSectionIds);
300
303
  const priceages = Object.values(groupedSeats).flatMap(group => group.priceages.map(pa => pa.priceage));
301
304
  if (priceages.length === 1 && (priceages[0].q === null || priceages[0].q >= desiredSeatQuantity) && !isSingleFlexi) {
302
305
  batchAddTicketsToCart(highlightedSeats.map(s => ({
@@ -324,7 +327,7 @@ function SeatMap(_ref) {
324
327
  }
325
328
  };
326
329
  const onMouseDown = (0, _react.useCallback)((s, hasSeatPrices) => {
327
- if ((mode === _utils.modes.DRAG || mode === _utils.modes.REMOVE) && canMultiSelect && hasSeatPrices && !selectedSeats.some(seat => seat.ssId === s.ssId) && s.s === (mode === _utils.modes.DRAG ? _utils.statuses.UNSOLD : _utils.statuses.USER_PENDING)) {
330
+ if ((mode === _utils.modes.DRAG || mode === _utils.modes.REMOVE) && canMultiSelect && hasSeatPrices && !selectedSeats.some(seat => seat.ssId === s.ssId) && (s.s === (mode === _utils.modes.DRAG ? _utils.statuses.UNSOLD : _utils.statuses.USER_PENDING) || s.s === _utils.statuses.RESERVED && canUseHolds && mode === _utils.modes.DRAG)) {
328
331
  setSelectedSeats(prev => [...prev, s]);
329
332
  }
330
333
  }, [mode, canMultiSelect, selectedSeats, setSelectedSeats]);
@@ -335,7 +338,7 @@ function SeatMap(_ref) {
335
338
  if (hideTooltipTimeoutRef.current) {
336
339
  clearTimeout(hideTooltipTimeoutRef.current);
337
340
  }
338
- if ((mode === _utils.modes.DRAG || mode === _utils.modes.REMOVE) && isDragging && canMultiSelect && hasSeatPrices && !selectedSeats.some(seat => seat.ssId === s.ssId) && s.s === (mode === _utils.modes.DRAG ? _utils.statuses.UNSOLD : _utils.statuses.USER_PENDING)) {
341
+ if ((mode === _utils.modes.DRAG || mode === _utils.modes.REMOVE) && isDragging && canMultiSelect && hasSeatPrices && !selectedSeats.some(seat => seat.ssId === s.ssId) && (s.s === (mode === _utils.modes.DRAG ? _utils.statuses.UNSOLD : _utils.statuses.USER_PENDING) || s.s === _utils.statuses.RESERVED && canUseHolds && mode === _utils.modes.DRAG)) {
339
342
  setSelectedSeats(prev => [...prev, s]);
340
343
  return;
341
344
  }
@@ -398,7 +401,8 @@ function SeatMap(_ref) {
398
401
  return ((_seats$pricing = seats.pricing) === null || _seats$pricing === void 0 ? void 0 : _seats$pricing.filter(p => p.psId === s.psId && (!priceSectionIds || priceSectionIds.includes(p.psId)) && (p.q === null || p.q > 0))) || [];
399
402
  }) : hoveredSeat ? ((_seats$pricing2 = seats.pricing) === null || _seats$pricing2 === void 0 ? void 0 : _seats$pricing2.filter(p => p.psId === hoveredSeat.psId && (!priceSectionIds || priceSectionIds.includes(p.psId)) && (p.q === null || p.q > 0))) || [] : undefined,
400
403
  prevHovered: prevHovered.current,
401
- isTouchScreen: isTouchScreen
404
+ isTouchScreen: isTouchScreen,
405
+ canUseHolds: canUseHolds
402
406
  }), /*#__PURE__*/_react.default.createElement(_Controls.default, {
403
407
  preventOrphanedSeats: seats === null || seats === void 0 ? void 0 : seats.preventOrphanedSeats,
404
408
  bounds: bounds,
@@ -432,7 +436,12 @@ function SeatMap(_ref) {
432
436
  lng: s.x,
433
437
  radius: 110
434
438
  };
435
- const seatPrices = ((_seats$pricing3 = seats.pricing) === null || _seats$pricing3 === void 0 ? void 0 : _seats$pricing3.filter(price => (!priceSectionIds || priceSectionIds.includes(price.psId)) && price.psId === s.psId)) || [];
439
+ const seatPrices = (((_seats$pricing3 = seats.pricing) === null || _seats$pricing3 === void 0 ? void 0 : _seats$pricing3.filter(price => (!priceSectionIds || priceSectionIds.includes(price.psId)) && price.psId === s.psId)) || []).map(p => {
440
+ const bookedQty = bookedSeats.filter(bs => bs.pId === p.paId && bs.psId === s.psId).length;
441
+ return _objectSpread(_objectSpread({}, p), {}, {
442
+ q: p.q === null ? null : Math.max(p.q - bookedQty, 0)
443
+ });
444
+ });
436
445
  const hasSeatPrices = (seatPrices === null || seatPrices === void 0 ? void 0 : seatPrices.length) > 0;
437
446
  const isSingleFlexi = seatPrices.length === 1 && !!seatPrices[0].pMax && !!seatPrices[0].pMin && seatPrices[0].pMax > seatPrices[0].pMin;
438
447
  const radiusInDeg = seatCenter.radius / _constants.ONE_DEG_LAT_IN_METERS;
@@ -467,7 +476,7 @@ function SeatMap(_ref) {
467
476
  bounds: iconBounds,
468
477
  opacity: s.loading || ticketPopupOpen && (chosenSeat === null || chosenSeat === void 0 || (_chosenSeat$seat2 = chosenSeat.seat) === null || _chosenSeat$seat2 === void 0 ? void 0 : _chosenSeat$seat2.ssId) === s.ssId ? 100 : 0,
469
478
  zIndex: 10
470
- }), !priceSectionIds || priceSectionIds.includes(s.psId) ? s.s === _utils.statuses.WHEELCHAIR_ACCESS ? /*#__PURE__*/_react.default.createElement(_reactLeaflet.ImageOverlay, {
479
+ }), !priceSectionIds || priceSectionIds.includes(s.psId) ? s.s === _utils.statuses.WHEELCHAIR_ACCESS && !isPOS ? /*#__PURE__*/_react.default.createElement(_reactLeaflet.ImageOverlay, {
471
480
  url: _encodedSvgs.wheelchairIcon,
472
481
  bounds: iconBounds,
473
482
  zIndex: 10
@@ -477,10 +486,14 @@ function SeatMap(_ref) {
477
486
  zIndex: 10
478
487
  }) : /*#__PURE__*/_react.default.createElement(_reactLeaflet.Circle, {
479
488
  center: [seatCenter.lat, seatCenter.lng],
480
- pathOptions: (0, _utils.getInitialColor)(s, price, s.loading || ticketPopupOpen && (chosenSeat === null || chosenSeat === void 0 || (_chosenSeat$seat3 = chosenSeat.seat) === null || _chosenSeat$seat3 === void 0 ? void 0 : _chosenSeat$seat3.ssId) === s.ssId, !hasSeatPrices, (selectedSeats.some(selectedSeat => selectedSeat.ssId === s.ssId) || highlightedSeats.some(hs => hs.ssId === s.ssId)) && (isOrphanMode ? isHoveringOnSeat : true), greyedOutSeats.some(gs => gs.ssId === s.ssId)),
489
+ pathOptions: (0, _utils.getInitialColor)(s, price, s.loading || ticketPopupOpen && (chosenSeat === null || chosenSeat === void 0 || (_chosenSeat$seat3 = chosenSeat.seat) === null || _chosenSeat$seat3 === void 0 ? void 0 : _chosenSeat$seat3.ssId) === s.ssId, canUseHolds, isPOS, !hasSeatPrices, (selectedSeats.some(selectedSeat => selectedSeat.ssId === s.ssId) || highlightedSeats.some(hs => hs.ssId === s.ssId)) && (isOrphanMode ? isHoveringOnSeat : true), greyedOutSeats.some(gs => gs.ssId === s.ssId)),
481
490
  radius: seatCenter.radius,
482
491
  eventHandlers: eventHandlers
483
- }, s.s === _utils.statuses.UNSOLD && hasSeatPrices ? /*#__PURE__*/_react.default.createElement(_reactLeaflet.Popup, {
492
+ }, (s.s === _utils.statuses.WHEELCHAIR_ACCESS || s.ost === _utils.statuses.WHEELCHAIR_ACCESS) && isPOS && /*#__PURE__*/_react.default.createElement(_reactLeaflet.ImageOverlay, {
493
+ url: _encodedSvgs.wheelchairIcon,
494
+ bounds: iconBounds,
495
+ zIndex: 10
496
+ }), (s.s === _utils.statuses.UNSOLD || s.s === _utils.statuses.RESERVED && canUseHolds || s.s === _utils.statuses.WHEELCHAIR_ACCESS && isPOS) && hasSeatPrices ? /*#__PURE__*/_react.default.createElement(_reactLeaflet.Popup, {
484
497
  className: "ticket-popup popup-".concat(s.sId)
485
498
  }, isSingleFlexi ? /*#__PURE__*/_react.default.createElement("div", {
486
499
  className: "single-flexi"
@@ -208,11 +208,12 @@ function PricingPopup(_ref4) {
208
208
  let {
209
209
  onClose,
210
210
  selectedSeats,
211
+ bookedSeats,
211
212
  batchAddTicketsToCart,
212
213
  pricing,
213
214
  priceSectionIds
214
215
  } = _ref4;
215
- const [seatGroupsToBook, setSeatGroupsToBook] = (0, _react.useState)(() => (0, _utils.groupSeatsByPriceSection)(selectedSeats, pricing, priceSectionIds));
216
+ const [seatGroupsToBook, setSeatGroupsToBook] = (0, _react.useState)(() => (0, _utils.groupSeatsByPriceSection)(selectedSeats, bookedSeats, pricing, priceSectionIds));
216
217
  const updateSeatGroupPriceage = (priceage, qty) => {
217
218
  const newSeatGroupsToBook = _objectSpread({}, seatGroupsToBook);
218
219
  const group = newSeatGroupsToBook[priceage.psId];
@@ -52,7 +52,8 @@ const SeatingPlan = _ref => {
52
52
  flexiAccent: "#e32664",
53
53
  flexiAccentDark: "#ffd3e2",
54
54
  flexiAccentLight: "#be003f"
55
- }
55
+ },
56
+ canManageHolds = false
56
57
  } = _ref;
57
58
  const [initialiseMessage, setInitialiseMessage] = (0, _react.useState)("Initialising seating plan...");
58
59
  const [isReloading, setIsReloading] = (0, _react.useState)(false);
@@ -78,7 +79,9 @@ const SeatingPlan = _ref => {
78
79
  const [invalidSeatsPopupOpen, setInvalidSeatsPopupOpen] = (0, _react.useState)(false);
79
80
  const [retrying, setRetrying] = (0, _react.useState)([]);
80
81
  const mapRef = (0, _react.useRef)(null);
81
- const canMultiSelect = bookingMode === _utils.bookingModes.POS;
82
+ const isPOS = bookingMode === _utils.bookingModes.POS;
83
+ const canMultiSelect = isPOS;
84
+ const canUseHolds = canManageHolds && isPOS;
82
85
  const apiUrl = "".concat(baseUrl, "/legacy/").concat(countryCode, "/shop/events/").concat(eventId, "/").concat(eventVenueId, "/showings/").concat(showingId, "/tickets/allocated/").concat(areaId);
83
86
  const setMap = map => {
84
87
  mapRef.current = map;
@@ -123,8 +126,9 @@ const SeatingPlan = _ref => {
123
126
  updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
124
127
  loading: true
125
128
  }));
126
- const isSeatAvailable = (s.s === _utils.statuses.UNSOLD || s.s === _utils.statuses.USER_PENDING) && !(bookedSeats.length >= quantity && s.s === _utils.statuses.UNSOLD) && (priceage.q === null || priceage.q > 0);
127
- if (!isSeatAvailable) {
129
+ const isSeatAvailable = s.s === _utils.statuses.UNSOLD || s.s === _utils.statuses.USER_PENDING || s.s === _utils.statuses.RESERVED && canUseHolds || s.s === _utils.statuses.WHEELCHAIR_ACCESS && isPOS;
130
+ const canInteract = !(bookedSeats.length >= quantity && isSeatAvailable) && (priceage.q === null || priceage.q > 0);
131
+ if (!canInteract) {
128
132
  if (bookedSeats.length >= quantity) {
129
133
  handleError({
130
134
  message: _utils.ERROR_MESSAGES.MAX_QUANTITY
@@ -227,10 +231,12 @@ const SeatingPlan = _ref => {
227
231
  };
228
232
  const removeTicketFromCart = async (s, e) => {
229
233
  const seatIndex = seats.seats.findIndex(seat => seat.ssId === s.ssId);
234
+ const isWheelchairSeat = s.ost === _utils.statuses.WHEELCHAIR_ACCESS;
230
235
  updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
231
236
  loading: true
232
237
  }));
233
238
  try {
239
+ var _s$ost;
234
240
  const response = await fetch("".concat(apiUrl, "/seat/").concat(s.ssId), {
235
241
  method: "DELETE",
236
242
  headers: {
@@ -242,7 +248,7 @@ const SeatingPlan = _ref => {
242
248
  }
243
249
  updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
244
250
  loading: false,
245
- s: _utils.statuses.UNSOLD,
251
+ s: s.ticketHold ? _utils.statuses.RESERVED : isWheelchairSeat ? _utils.statuses.WHEELCHAIR_ACCESS : (_s$ost = s.ost) !== null && _s$ost !== void 0 ? _s$ost : _utils.statuses.UNSOLD,
246
252
  bookedPrice: null
247
253
  }));
248
254
  setBookedSeats(prev => prev.filter(bs => bs.ssId !== s.ssId));
@@ -266,106 +272,179 @@ const SeatingPlan = _ref => {
266
272
  return;
267
273
  }
268
274
  setSeatsLoading(seatsToBook.map(seat => seat.seat), true);
269
- const succeeded = [];
270
- const _tryBook = async function tryBook(seatToBook) {
271
- let retryCount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
275
+ if (isPOS) {
272
276
  try {
273
277
  const res = await fetch(apiUrl, {
274
278
  method: "POST",
275
- body: JSON.stringify({
279
+ body: JSON.stringify(seatsToBook.map(seatToBook => ({
276
280
  priceAgeId: seatToBook.priceage.paId,
277
281
  seatId: seatToBook.seat.ssId,
278
282
  price: seatToBook.priceage.p,
279
283
  connectedShowings: connectedShowings
280
- }),
284
+ }))),
281
285
  headers: {
282
286
  "content-type": "application/json",
283
287
  "basket-key": sessionId
284
288
  }
285
289
  });
286
- if (res.status === 429) {
287
- if (retryCount < _constants.BOOKING_MAX_RETRIES) {
288
- setRetrying(prev => [...prev, seatToBook.seat.ssId]);
289
- const delay = (0, _utils.getRetryDelay)(res);
290
- await new Promise(resolve => setTimeout(resolve, delay));
291
- return _tryBook(seatToBook, retryCount + 1);
292
- } else {
293
- throw new Error("429");
294
- }
295
- } else if (!res.ok) {
290
+ if (!res.ok) {
296
291
  throw new Error("".concat(res.status));
297
292
  }
298
- succeeded.push(seatToBook.seat);
299
- } catch (_unused3) {
300
- throw new Error("Booking failed for seat ".concat(seatToBook.seat.r, " ").concat(seatToBook.seat.c));
301
- }
302
- };
303
- try {
304
- for (const seatToBook of seatsToBook) {
305
- await _tryBook(seatToBook);
306
- }
307
- setRetrying(prev => prev.filter(id => !seatsToBook.some(s => s.seat.ssId === id)));
308
- setBookedSeats(prev => [...prev, ...seatsToBook.map(_ref2 => {
309
- let {
310
- seat,
311
- priceage
312
- } = _ref2;
313
- return _objectSpread(_objectSpread({}, seat), {}, {
314
- showingId: showingId,
315
- pId: priceage.paId,
316
- p: priceage.p,
317
- paName: priceage.paName
318
- });
319
- })]);
320
- setSeats(prev => {
321
- const updatedSeats = _objectSpread({}, prev);
322
- seatsToBook.forEach(_ref3 => {
293
+ setBookedSeats(prev => [...prev, ...seatsToBook.map(_ref2 => {
323
294
  let {
324
- seat
325
- } = _ref3;
326
- const seatIndex = updatedSeats.seats.findIndex(s => s.ssId === seat.ssId);
327
- if (seatIndex !== -1) {
328
- updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, updatedSeats.seats[seatIndex]), {}, {
329
- loading: false,
330
- s: _utils.statuses.USER_PENDING,
331
- bookedPrice: price
295
+ seat,
296
+ priceage
297
+ } = _ref2;
298
+ return _objectSpread(_objectSpread({}, seat), {}, {
299
+ showingId: showingId,
300
+ pId: priceage.paId,
301
+ p: priceage.p,
302
+ paName: priceage.paName
303
+ });
304
+ })]);
305
+ setSeats(prev => {
306
+ const updatedSeats = _objectSpread({}, prev);
307
+ seatsToBook.forEach(_ref3 => {
308
+ let {
309
+ seat
310
+ } = _ref3;
311
+ const seatIndex = updatedSeats.seats.findIndex(s => s.ssId === seat.ssId);
312
+ if (seatIndex !== -1) {
313
+ updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, updatedSeats.seats[seatIndex]), {}, {
314
+ loading: false,
315
+ s: _utils.statuses.USER_PENDING,
316
+ bookedPrice: price
317
+ });
318
+ }
319
+ });
320
+ return updatedSeats;
321
+ });
322
+ const event = {
323
+ type: "cart-change-add",
324
+ details: [...seatsToBook.map(_ref4 => {
325
+ let {
326
+ seat,
327
+ priceage
328
+ } = _ref4;
329
+ return _objectSpread(_objectSpread({}, seat), {}, {
330
+ showingId: showingId,
331
+ pId: priceage.paId,
332
+ p: priceage.p,
333
+ paName: priceage.paName
332
334
  });
333
- }
335
+ })]
336
+ };
337
+ callbackFunction === null || callbackFunction === void 0 || callbackFunction(event);
338
+ } catch (error) {
339
+ console.error("Failed to book seats:", error);
340
+ setSeatsLoading(seatsToBook.map(seat => seat.seat), false);
341
+ handleError({
342
+ code: error === "429" ? 429 : 500,
343
+ message: _utils.ERROR_MESSAGES.GENERIC_ERROR
334
344
  });
335
- return updatedSeats;
336
- });
337
- const event = {
338
- type: "cart-change-add",
339
- details: [...seatsToBook.map(_ref4 => {
345
+ }
346
+ } else {
347
+ const succeeded = [];
348
+ const _tryBook = async function tryBook(seatToBook) {
349
+ let retryCount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
350
+ try {
351
+ const res = await fetch(apiUrl, {
352
+ method: "POST",
353
+ body: JSON.stringify({
354
+ priceAgeId: seatToBook.priceage.paId,
355
+ seatId: seatToBook.seat.ssId,
356
+ price: seatToBook.priceage.p,
357
+ connectedShowings: connectedShowings
358
+ }),
359
+ headers: {
360
+ "content-type": "application/json",
361
+ "basket-key": sessionId
362
+ }
363
+ });
364
+ if (res.status === 429) {
365
+ if (retryCount < _constants.BOOKING_MAX_RETRIES) {
366
+ setRetrying(prev => [...prev, seatToBook.seat.ssId]);
367
+ const delay = (0, _utils.getRetryDelay)(res);
368
+ await new Promise(resolve => setTimeout(resolve, delay));
369
+ return _tryBook(seatToBook, retryCount + 1);
370
+ } else {
371
+ throw new Error("429");
372
+ }
373
+ } else if (!res.ok) {
374
+ throw new Error("".concat(res.status));
375
+ }
376
+ succeeded.push(seatToBook.seat);
377
+ } catch (_unused3) {
378
+ throw new Error("Booking failed for seat ".concat(seatToBook.seat.r, " ").concat(seatToBook.seat.c));
379
+ }
380
+ };
381
+ try {
382
+ for (const seatToBook of seatsToBook) {
383
+ await _tryBook(seatToBook);
384
+ }
385
+ setRetrying(prev => prev.filter(id => !seatsToBook.some(s => s.seat.ssId === id)));
386
+ setBookedSeats(prev => [...prev, ...seatsToBook.map(_ref5 => {
340
387
  let {
341
388
  seat,
342
389
  priceage
343
- } = _ref4;
390
+ } = _ref5;
344
391
  return _objectSpread(_objectSpread({}, seat), {}, {
345
392
  showingId: showingId,
346
393
  pId: priceage.paId,
347
394
  p: priceage.p,
348
395
  paName: priceage.paName
349
396
  });
350
- })]
351
- };
352
- callbackFunction === null || callbackFunction === void 0 || callbackFunction(event);
353
- } catch (error) {
354
- setRetrying(prev => prev.filter(id => !seatsToBook.some(s => s.seat.ssId === id)));
355
- console.error("Failed to book seats:", error);
356
- for (const seat of succeeded) {
357
- await fetch("".concat(apiUrl, "/seat/").concat(seat.ssId), {
358
- method: "DELETE",
359
- headers: {
360
- "basket-key": sessionId
361
- }
397
+ })]);
398
+ setSeats(prev => {
399
+ const updatedSeats = _objectSpread({}, prev);
400
+ seatsToBook.forEach(_ref6 => {
401
+ let {
402
+ seat
403
+ } = _ref6;
404
+ const seatIndex = updatedSeats.seats.findIndex(s => s.ssId === seat.ssId);
405
+ if (seatIndex !== -1) {
406
+ updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, updatedSeats.seats[seatIndex]), {}, {
407
+ loading: false,
408
+ s: _utils.statuses.USER_PENDING,
409
+ bookedPrice: price
410
+ });
411
+ }
412
+ });
413
+ return updatedSeats;
414
+ });
415
+ const event = {
416
+ type: "cart-change-add",
417
+ details: [...seatsToBook.map(_ref7 => {
418
+ let {
419
+ seat,
420
+ priceage
421
+ } = _ref7;
422
+ return _objectSpread(_objectSpread({}, seat), {}, {
423
+ showingId: showingId,
424
+ pId: priceage.paId,
425
+ p: priceage.p,
426
+ paName: priceage.paName
427
+ });
428
+ })]
429
+ };
430
+ callbackFunction === null || callbackFunction === void 0 || callbackFunction(event);
431
+ } catch (error) {
432
+ setRetrying(prev => prev.filter(id => !seatsToBook.some(s => s.seat.ssId === id)));
433
+ console.error("Failed to book seats:", error);
434
+ for (const seat of succeeded) {
435
+ await fetch("".concat(apiUrl, "/seat/").concat(seat.ssId), {
436
+ method: "DELETE",
437
+ headers: {
438
+ "basket-key": sessionId
439
+ }
440
+ });
441
+ }
442
+ setSeatsLoading(seatsToBook.map(seat => seat.seat), false);
443
+ handleError({
444
+ code: error === "429" ? 429 : 500,
445
+ message: _utils.ERROR_MESSAGES.GENERIC_ERROR
362
446
  });
363
447
  }
364
- setSeatsLoading(seatsToBook.map(seat => seat.seat), false);
365
- handleError({
366
- code: error === "429" ? 429 : 500,
367
- message: _utils.ERROR_MESSAGES.GENERIC_ERROR
368
- });
369
448
  }
370
449
  };
371
450
  const batchRemoveTicketsFromCart = async seatsToRemove => {
@@ -403,9 +482,10 @@ const SeatingPlan = _ref => {
403
482
  seatsToRemove.forEach(s => {
404
483
  const seatIndex = updatedSeats.seats.findIndex(seat => seat.ssId === s.ssId);
405
484
  if (seatIndex !== -1) {
485
+ var _s$ost2;
406
486
  updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, updatedSeats.seats[seatIndex]), {}, {
407
487
  loading: false,
408
- s: succeededIds.includes(s.ssId) ? _utils.statuses.UNSOLD : s.s,
488
+ s: succeededIds.includes(s.ssId) ? s.ticketHold ? _utils.statuses.RESERVED : (_s$ost2 = s.ost) !== null && _s$ost2 !== void 0 ? _s$ost2 : _utils.statuses.UNSOLD : s.s,
409
489
  bookedPrice: null
410
490
  });
411
491
  }
@@ -426,7 +506,7 @@ const SeatingPlan = _ref => {
426
506
  circle: e,
427
507
  seat: s
428
508
  });
429
- if (s.s !== _utils.statuses.UNSOLD && s.s !== _utils.statuses.USER_PENDING || bookedSeats.length >= quantity && s.s === _utils.statuses.UNSOLD) {
509
+ if (s.s !== _utils.statuses.UNSOLD && s.s !== _utils.statuses.USER_PENDING && (s.s !== _utils.statuses.RESERVED || canUseHolds) && (s.s !== _utils.statuses.WHEELCHAIR_ACCESS || !isPOS) || bookedSeats.length >= quantity && s.s === _utils.statuses.UNSOLD) {
430
510
  if (bookedSeats.length >= quantity) {
431
511
  handleError({
432
512
  message: _utils.ERROR_MESSAGES.MAX_QUANTITY
@@ -434,7 +514,7 @@ const SeatingPlan = _ref => {
434
514
  }
435
515
  return;
436
516
  }
437
- if (s.s === _utils.statuses.UNSOLD) {
517
+ if (s.s === _utils.statuses.UNSOLD || s.s === _utils.statuses.RESERVED && canUseHolds || s.s === _utils.statuses.WHEELCHAIR_ACCESS && isPOS) {
438
518
  var _seats$pricing;
439
519
  const availablePrices = ((_seats$pricing = seats.pricing) === null || _seats$pricing === void 0 ? void 0 : _seats$pricing.filter(price => (!priceSectionIds || priceSectionIds.includes(price.psId)) && s.psId === price.psId)) || [];
440
520
  const isSingleFlexi = availablePrices.length === 1 && !!availablePrices[0].pMax && !!availablePrices[0].pMin && availablePrices[0].pMax > availablePrices[0].pMin;
@@ -481,16 +561,23 @@ const SeatingPlan = _ref => {
481
561
  throw new Error("Something went wrong. Please try again or contact support.");
482
562
  }
483
563
  }
484
- setBookedSeats(data.seats.filter(s => s.s === _utils.statuses.USER_PENDING && s.bookedPrice === price).map(s => ({
485
- ssId: s.ssId,
486
- r: s.r,
487
- c: s.c,
488
- showingId: showingId,
489
- p: s.p,
490
- //price || s.defaultPrice,
491
- pId: s.pId,
492
- paName: s.paName
493
- })));
564
+
565
+ // setBookedSeats(
566
+ // data.seats
567
+ // .filter(
568
+ // (s) => s.s === statuses.USER_PENDING && s.bookedPrice === price,
569
+ // )
570
+ // .map((s) => ({
571
+ // ssId: s.ssId,
572
+ // r: s.r,
573
+ // c: s.c,
574
+ // showingId: showingId,
575
+ // p: s.p, //price || s.defaultPrice,
576
+ // pId: s.pId,
577
+ // paName: s.paName,
578
+ // })),
579
+ // );
580
+
494
581
  const img = new Image();
495
582
  const setupSeats = (h, w) => {
496
583
  const lat1 = 0;
@@ -576,6 +663,7 @@ const SeatingPlan = _ref => {
576
663
  setSelectedSeats([]);
577
664
  },
578
665
  selectedSeats: selectedSeats,
666
+ bookedSeats: bookedSeats,
579
667
  pricing: seats.pricing,
580
668
  priceSectionIds: priceSectionIds,
581
669
  batchAddTicketsToCart: batchAddTicketsToCart
@@ -639,7 +727,9 @@ const SeatingPlan = _ref => {
639
727
  setSeatsToRemove: setSeatsToRemove,
640
728
  setRemoveMultipleSeatsPopupOpen: setRemoveMultipleSeatsPopupOpen,
641
729
  setInvalidSeatsPopupOpen: setInvalidSeatsPopupOpen,
642
- batchAddTicketsToCart: batchAddTicketsToCart
730
+ batchAddTicketsToCart: batchAddTicketsToCart,
731
+ canUseHolds: canUseHolds,
732
+ bookingMode: bookingMode
643
733
  }))), mapContainer)) : /*#__PURE__*/_react.default.createElement("div", {
644
734
  className: "loading"
645
735
  }, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("svg", {
@@ -34,6 +34,8 @@ button {
34
34
  --flexi-accent-light: rgb(255 211 226);
35
35
  --flexi-accent-dark: rgb(190 0 63); */
36
36
  font-family: inherit;
37
+ height: 100%;
38
+ width: 100%;
37
39
  }
38
40
 
39
41
  .legendBox {
@@ -1547,6 +1549,26 @@ svg.leaflet-image-layer.leaflet-interactive path {
1547
1549
  align-items: center;
1548
1550
  }
1549
1551
 
1552
+ .hover-popup-ticket-hold {
1553
+ padding: 0.5rem;
1554
+ padding-top: 0;
1555
+ font-size: 0.5rem;
1556
+ }
1557
+
1558
+ .hover-popup-ticket-hold .held-by {
1559
+ display: flex;
1560
+ gap: 0.25rem;
1561
+ align-items: center;
1562
+ }
1563
+
1564
+ .hover-popup-ticket-hold .ticket-hold-category {
1565
+ font-weight: 500;
1566
+ padding: 0.05rem 0.25rem;
1567
+ border-radius: 0.5rem;
1568
+ width: fit-content;
1569
+ font-size: 0.625rem;
1570
+ }
1571
+
1550
1572
  .hover-popup-price {
1551
1573
  padding: 0.5rem;
1552
1574
  font-size: 0.875rem;
@@ -6,6 +6,11 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.groupSeatsByPriceSection = exports.getValidSeats = exports.getSeatCenterLatLng = exports.getRowSection = exports.getRetryDelay = exports.getIsTouchScreen = exports.getInitialColor = exports.getAdjacentBookedSeats = exports.createsOrphan = exports.canSelectSingleSeatGivenOrphanRules = exports.NZDollar = void 0;
7
7
  var _enums = require("./enums");
8
8
  var _constants = require("./constants");
9
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
10
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
11
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
12
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
13
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
9
14
  const getIsTouchScreen = () => {
10
15
  let hasTouchScreen = false;
11
16
  if (typeof window !== "undefined") {
@@ -18,7 +23,8 @@ const getIsTouchScreen = () => {
18
23
  return hasTouchScreen;
19
24
  };
20
25
  exports.getIsTouchScreen = getIsTouchScreen;
21
- const getInitialColor = (s, price, loading, disabled, selected, greyedOut) => {
26
+ const getInitialColor = (s, price, loading, canManageHolds, isPOS, disabled, selected, greyedOut) => {
27
+ const isWheelchairAccess = s.s === _enums.statuses.WHEELCHAIR_ACCESS || s.ost === _enums.statuses.WHEELCHAIR_ACCESS;
22
28
  // selected state for multiselect
23
29
  if (selected) {
24
30
  return {
@@ -37,9 +43,9 @@ const getInitialColor = (s, price, loading, disabled, selected, greyedOut) => {
37
43
  };
38
44
  }
39
45
  return {
40
- fillColor: s.s === _enums.statuses.UNSOLD && !disabled ? _enums.statusColors.available : s.s === _enums.statuses.USER_PENDING ? s.bookedPrice === price ? _enums.statusColors.booked : _enums.statusColors.bookedWithDifferentPrice : _enums.statusColors.sold,
46
+ fillColor: (s.s === _enums.statuses.UNSOLD || s.s === _enums.statuses.WHEELCHAIR_ACCESS && isPOS) && !disabled ? _enums.statusColors.available : s.s === _enums.statuses.USER_PENDING ? s.bookedPrice === price ? _enums.statusColors.booked : _enums.statusColors.bookedWithDifferentPrice : s.s === _enums.statuses.RESERVED && s.ticketHold && canManageHolds ? s.ticketHold.TicketHoldCategoryColor : _enums.statusColors.sold,
41
47
  color: "none",
42
- fillOpacity: loading ? 0 : 1,
48
+ fillOpacity: loading ? 0 : isWheelchairAccess && isPOS ? 0.5 : 1,
43
49
  stroke: false
44
50
  };
45
51
  };
@@ -227,12 +233,18 @@ const getRetryDelay = response => {
227
233
  return delay + Math.random() * 1000;
228
234
  };
229
235
  exports.getRetryDelay = getRetryDelay;
230
- const groupSeatsByPriceSection = (seats, pricing, priceSectionIds) => {
236
+ const groupSeatsByPriceSection = (seats, bookedSeats, pricing, priceSectionIds) => {
231
237
  const groupedSeats = seats.reduce((acc, curr) => {
232
238
  if (curr.psId) {
233
239
  const priceages = pricing.filter(price => price.psId === curr.psId && (!priceSectionIds || priceSectionIds.includes(price.psId)));
234
- const availablePriceages = priceages.filter(p => p.q === null || p.q > 0);
235
- const soldOutPriceages = priceages.filter(p => p.q === 0);
240
+ const adjustedPriceages = priceages.map(p => {
241
+ const bookedQty = bookedSeats.filter(s => s.pId === p.paId && s.psId === curr.psId).length;
242
+ return _objectSpread(_objectSpread({}, p), {}, {
243
+ q: p.q !== null ? Math.max(p.q - bookedQty, 0) : null
244
+ });
245
+ });
246
+ const availablePriceages = adjustedPriceages.filter(p => p.q === null || p.q > 0);
247
+ const soldOutPriceages = adjustedPriceages.filter(p => p.q === 0);
236
248
  const numSeats = seats.filter(s => s.psId === curr.psId).length;
237
249
  if (!acc[curr.psId]) {
238
250
  let qtyFilled = 0;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "iticket-seatingplan-dev",
3
3
  "description": "Seating plan with FLEXi pricing",
4
4
  "author": "gedwyne",
5
- "version": "2.0.8",
5
+ "version": "2.0.18",
6
6
  "private": false,
7
7
  "keywords": [
8
8
  "iticket",
@@ -20,10 +20,10 @@
20
20
  "@types/leaflet-draw": "^1.0.11",
21
21
  "leaflet": "^1.9.4",
22
22
  "leaflet-draw": "^1.0.4",
23
- "react": "^19.0.0",
24
- "react-dom": "^19.0.0",
25
- "react-leaflet": "^5.0.0-rc.2",
26
- "react-leaflet-draw": "^0.20.4",
23
+ "react": "^19.2.1",
24
+ "react-dom": "^19.2.1",
25
+ "react-leaflet": "^5.0.0",
26
+ "react-leaflet-draw": "0.20.3",
27
27
  "react-scripts": "^5.0.1"
28
28
  },
29
29
  "codependencies": {
@@ -58,4 +58,4 @@
58
58
  "@types/react-dom": "^18.3.1",
59
59
  "typescript": "^5.7.2"
60
60
  }
61
- }
61
+ }