iticket-seatingplan-dev 1.6.9 → 1.7.1

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.
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = InvalidSeatsPopup;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _encodedSvgs = require("./assets/encodedSvgs");
9
+ var _TicketIcon = _interopRequireDefault(require("./icons/TicketIcon"));
10
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
+ function InvalidSeatsPopup(_ref) {
12
+ let {
13
+ onClose
14
+ } = _ref;
15
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
16
+ className: "seatingplan-dialog-backdrop",
17
+ onClick: onClose
18
+ }), /*#__PURE__*/_react.default.createElement("div", {
19
+ className: "seatingplan-dialog remove-multiple-seats-popup",
20
+ role: "dialog",
21
+ "aria-modal": "true"
22
+ }, /*#__PURE__*/_react.default.createElement("div", {
23
+ className: "popup-header"
24
+ }, /*#__PURE__*/_react.default.createElement(_TicketIcon.default, {
25
+ className: "ticket-icon",
26
+ height: "86px",
27
+ width: "86px"
28
+ }), /*#__PURE__*/_react.default.createElement("button", {
29
+ className: "close-button",
30
+ "aria-label": "close popup",
31
+ onClick: onClose
32
+ }, /*#__PURE__*/_react.default.createElement("img", {
33
+ src: _encodedSvgs.closeIconWhite,
34
+ alt: "close",
35
+ height: "20px",
36
+ width: "20px"
37
+ })), /*#__PURE__*/_react.default.createElement("h3", null, "Seat selection")), /*#__PURE__*/_react.default.createElement("div", {
38
+ className: "seatingplan-dialog-content"
39
+ }, /*#__PURE__*/_react.default.createElement("h4", {
40
+ className: "quantity-heading"
41
+ }, "The seats you've chosen aren't available. They may leave a single seat on its own, or there may not be enough seats in the row for your selection. Please select different seats to continue."), /*#__PURE__*/_react.default.createElement("div", {
42
+ className: "buttons buttons-single"
43
+ }, /*#__PURE__*/_react.default.createElement("button", {
44
+ className: "add-cart",
45
+ onClick: onClose
46
+ }, "Ok")))));
47
+ }
@@ -59,10 +59,14 @@ function SeatMap(_ref) {
59
59
  setSelectQuantityPopupOpen,
60
60
  desiredSeatQuantity,
61
61
  setSeatsToRemove,
62
- setRemoveMultipleSeatsPopupOpen
62
+ setRemoveMultipleSeatsPopupOpen,
63
+ setInvalidSeatsPopupOpen
63
64
  } = _ref;
64
65
  const [isLegendOpen, setIsLegendOpen] = (0, _react.useState)(false);
65
66
  const [isDragging, setIsDragging] = (0, _react.useState)(false);
67
+ const [highlightedSeats, setHighlightedSeats] = (0, _react.useState)([]);
68
+ const [greyedOutSeats, setGreyedOutSeats] = (0, _react.useState)([]);
69
+ const [clickedSeat, setClickedSeat] = (0, _react.useState)(null);
66
70
  const drawLayersRef = (0, _react.useRef)(null);
67
71
  const drawRef = (0, _react.useRef)(null);
68
72
  const popupRef = (0, _react.useRef)(null);
@@ -421,9 +425,7 @@ function SeatMap(_ref) {
421
425
  zIndex: 10
422
426
  }) : /*#__PURE__*/_react.default.createElement(_reactLeaflet.Circle, {
423
427
  center: [s.r.includes("NORTH") ? getNorthSeatLat(s) : seatCenter.lat, seatCenter.lng],
424
- pathOptions: (0, _utils.getInitialColor)(s, price, !hasSeatPrices,
425
- // TEST selected state for multiselect
426
- !!selectedSeats.find(selectedSeat => selectedSeat.ssId === s.ssId)),
428
+ pathOptions: (0, _utils.getInitialColor)(s, price, !hasSeatPrices, selectedSeats.some(selectedSeat => selectedSeat.ssId === s.ssId) || highlightedSeats.some(hs => hs.ssId === s.ssId), greyedOutSeats.some(gs => gs.ssId === s.ssId)),
427
429
  radius: 12000,
428
430
  eventHandlers: {
429
431
  click: e => {
@@ -431,12 +433,22 @@ function SeatMap(_ref) {
431
433
  map.closePopup();
432
434
  if (s.s === _utils.statuses.USER_PENDING) {
433
435
  const newSeatsToRemove = (0, _utils.getAdjacentBookedSeats)(s, seatsMap);
434
- setSeatsToRemove(newSeatsToRemove);
435
- setRemoveMultipleSeatsPopupOpen(true);
436
+ if (newSeatsToRemove.length === 1) {
437
+ handleClickSeat(e, s);
438
+ } else {
439
+ setSeatsToRemove(newSeatsToRemove);
440
+ setRemoveMultipleSeatsPopupOpen(true);
441
+ }
442
+ } else if (highlightedSeats.length > 0 && highlightedSeats.length === desiredSeatQuantity) {
443
+ if ((0, _utils.isTouchScreen)() && clickedSeat !== s.ssId) {
444
+ setClickedSeat(s.ssId);
445
+ } else {
446
+ setSelectedSeats([...highlightedSeats]);
447
+ setPricingPopupOpen(true);
448
+ }
449
+ } else if (greyedOutSeats.some(gs => gs.ssId === s.ssId)) {
450
+ setInvalidSeatsPopupOpen(true);
436
451
  }
437
- if (selectedSeats.length > 0 && selectedSeats.length === desiredSeatQuantity) {
438
- setPricingPopupOpen(true);
439
- } else if (s.s === _utils.statuses.USER_PENDING) {}
440
452
  } else if (mode === _utils.modes.SINGLE && hasSeatPrices) {
441
453
  handleClickSeat(e, s);
442
454
  }
@@ -462,12 +474,26 @@ function SeatMap(_ref) {
462
474
  setSelectedSeats(prev => [...prev, s]);
463
475
  }
464
476
  } else {
465
- setSelectedSeats([]);
466
477
  if (seats.preventOrphanedSeats && desiredSeatQuantity > 0 && s.s === _utils.statuses.UNSOLD) {
467
478
  const seatsToBook = (0, _utils.getValidSeats)(s, seatsMap, desiredSeatQuantity);
468
- setSelectedSeats(seatsToBook);
479
+ if (seatsToBook.valid) {
480
+ setHighlightedSeats(seatsToBook.seats);
481
+ } else {
482
+ setGreyedOutSeats(seatsToBook.seats);
483
+ }
469
484
  }
470
485
  }
486
+ },
487
+ mouseout: () => {
488
+ if (highlightedSeats.length > 0) {
489
+ setHighlightedSeats([]);
490
+ }
491
+ if (greyedOutSeats.length > 0) {
492
+ setGreyedOutSeats([]);
493
+ }
494
+ if (clickedSeat) {
495
+ setClickedSeat(null);
496
+ }
471
497
  }
472
498
  }
473
499
  // value={s}
@@ -62,21 +62,21 @@ const PriceSelect = _ref => {
62
62
  };
63
63
  function PricingPopup(_ref2) {
64
64
  let {
65
- cancel,
65
+ onClose,
66
66
  groupedSelectedSeats,
67
67
  pricing,
68
68
  priceSectionIds,
69
69
  batchAddTicketsToCart,
70
70
  selectedSeatsWithPricing,
71
- singleSeats
71
+ isSingleSeats
72
72
  } = _ref2;
73
73
  const [seatGroupsToBook, setSeatGroupsToBook] = (0, _react.useState)(groupedSelectedSeats);
74
74
  const [singleSeatsToBook, setSingleSeatsToBook] = (0, _react.useState)(selectedSeatsWithPricing);
75
75
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
76
- className: "pricing-popup-backdrop",
77
- onClick: cancel
76
+ className: "seatingplan-dialog-backdrop",
77
+ onClick: onClose
78
78
  }), /*#__PURE__*/_react.default.createElement("div", {
79
- className: "pricing-popup",
79
+ className: "seatingplan-dialog",
80
80
  role: "dialog",
81
81
  "aria-modal": "true"
82
82
  }, /*#__PURE__*/_react.default.createElement("div", {
@@ -88,17 +88,17 @@ function PricingPopup(_ref2) {
88
88
  }), /*#__PURE__*/_react.default.createElement("button", {
89
89
  className: "close-button",
90
90
  "aria-label": "close popup",
91
- onClick: cancel
91
+ onClick: onClose
92
92
  }, /*#__PURE__*/_react.default.createElement("img", {
93
93
  src: _encodedSvgs.closeIconWhite,
94
94
  alt: "close",
95
95
  height: "20px",
96
96
  width: "20px"
97
97
  })), /*#__PURE__*/_react.default.createElement("h3", null, "Choose prices")), /*#__PURE__*/_react.default.createElement("div", {
98
- className: "pricing-popup-content"
98
+ className: "seatingplan-dialog-content"
99
99
  }, /*#__PURE__*/_react.default.createElement("div", {
100
100
  className: "seat-list"
101
- }, !singleSeats && groupedSelectedSeats ? Object.entries(seatGroupsToBook).map(_ref3 => {
101
+ }, !isSingleSeats && groupedSelectedSeats ? Object.entries(seatGroupsToBook).map(_ref3 => {
102
102
  let [id, seatGroup] = _ref3;
103
103
  const seatPriceages = pricing.filter(price => (!priceSectionIds || priceSectionIds.includes(price.psId)) && price.psId === parseInt(id));
104
104
  return /*#__PURE__*/_react.default.createElement("div", {
@@ -119,7 +119,7 @@ function PricingPopup(_ref2) {
119
119
  }) : /*#__PURE__*/_react.default.createElement("p", {
120
120
  className: "no-prices-available"
121
121
  }, "Oops, looks like there are no prices available for this section."));
122
- }) : singleSeats && selectedSeatsWithPricing ? selectedSeatsWithPricing.map(_ref4 => {
122
+ }) : isSingleSeats && selectedSeatsWithPricing ? selectedSeatsWithPricing.map(_ref4 => {
123
123
  let {
124
124
  seat,
125
125
  priceage
@@ -151,16 +151,16 @@ function PricingPopup(_ref2) {
151
151
  className: "buttons"
152
152
  }, /*#__PURE__*/_react.default.createElement("button", {
153
153
  className: "cancel",
154
- onClick: cancel
154
+ onClick: onClose
155
155
  }, "Cancel"), /*#__PURE__*/_react.default.createElement("button", {
156
156
  className: "add-cart",
157
157
  onClick: () => {
158
- singleSeats ? batchAddTicketsToCart(singleSeatsToBook) : batchAddTicketsToCart(Object.values(seatGroupsToBook).filter(v => !!v.priceage).flatMap(s => s.seats.map(seat => ({
158
+ isSingleSeats ? batchAddTicketsToCart(singleSeatsToBook) : batchAddTicketsToCart(Object.values(seatGroupsToBook).filter(v => !!v.priceage).flatMap(s => s.seats.map(seat => ({
159
159
  seat,
160
160
  priceage: s.priceage
161
161
  }))));
162
- cancel();
162
+ onClose();
163
163
  },
164
- disabled: singleSeats ? singleSeatsToBook.every(v => !v.priceage) : Object.values(seatGroupsToBook).every(v => !v.priceage)
164
+ disabled: isSingleSeats ? singleSeatsToBook.every(v => !v.priceage) : Object.values(seatGroupsToBook).every(v => !v.priceage)
165
165
  }, "Add to cart")))));
166
166
  }
@@ -14,10 +14,10 @@ function RemoveMultipleSeatsPopup(_ref) {
14
14
  onClose
15
15
  } = _ref;
16
16
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
17
- className: "pricing-popup-backdrop",
17
+ className: "seatingplan-dialog-backdrop",
18
18
  onClick: onClose
19
19
  }), /*#__PURE__*/_react.default.createElement("div", {
20
- className: "pricing-popup remove-multiple-seats-popup",
20
+ className: "seatingplan-dialog remove-multiple-seats-popup",
21
21
  role: "dialog",
22
22
  "aria-modal": "true"
23
23
  }, /*#__PURE__*/_react.default.createElement("div", {
@@ -36,7 +36,7 @@ function RemoveMultipleSeatsPopup(_ref) {
36
36
  height: "20px",
37
37
  width: "20px"
38
38
  })), /*#__PURE__*/_react.default.createElement("h3", null, "Remove from cart")), /*#__PURE__*/_react.default.createElement("div", {
39
- className: "pricing-popup-content"
39
+ className: "seatingplan-dialog-content"
40
40
  }, /*#__PURE__*/_react.default.createElement("h4", {
41
41
  className: "quantity-heading"
42
42
  }, "This will remove all adjacent seats from your cart. Are you sure you want to proceed?"), /*#__PURE__*/_react.default.createElement("div", {
@@ -15,6 +15,7 @@ var _reactDom = require("react-dom");
15
15
  var _PricingPopup = _interopRequireDefault(require("./PricingPopup"));
16
16
  var _SelectQuantityPopup = _interopRequireDefault(require("./SelectQuantityPopup"));
17
17
  var _RemoveMultipleSeatsPopup = _interopRequireDefault(require("./RemoveMultipleSeatsPopup"));
18
+ var _InvalidSeatsPopup = _interopRequireDefault(require("./InvalidSeatsPopup"));
18
19
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
19
20
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
20
21
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
@@ -64,6 +65,7 @@ const SeatingPlan = _ref => {
64
65
  const [selectedSeats, setSelectedSeats] = (0, _react.useState)([]);
65
66
  const [seatsToRemove, setSeatsToRemove] = (0, _react.useState)([]);
66
67
  const [removeMultipleSeatsPopupOpen, setRemoveMultipleSeatsPopupOpen] = (0, _react.useState)(false);
68
+ const [invalidSeatsPopupOpen, setInvalidSeatsPopupOpen] = (0, _react.useState)(false);
67
69
  const mapRef = (0, _react.useRef)(null);
68
70
  const canMultiSelect = bookingMode === _utils.bookingModes.POS;
69
71
  const apiUrl = "".concat(baseUrl, "/legacy/").concat(countryCode, "/shop/events/").concat(eventId, "/").concat(eventVenueId, "/showings/").concat(showingId, "/tickets/allocated/").concat(areaId);
@@ -290,7 +292,7 @@ const SeatingPlan = _ref => {
290
292
  try {
291
293
  for (let seat of seatsToBook) {
292
294
  try {
293
- await fetch(apiUrl, {
295
+ const res = await fetch(apiUrl, {
294
296
  method: "POST",
295
297
  body: JSON.stringify({
296
298
  priceAgeId: seat.priceage.paId,
@@ -303,6 +305,9 @@ const SeatingPlan = _ref => {
303
305
  "basket-key": sessionId
304
306
  }
305
307
  });
308
+ if (!res.ok) {
309
+ throw new Error("".concat(res.status));
310
+ }
306
311
  succeeded.push(seat);
307
312
  } catch (error) {
308
313
  console.error("Failed to book seat ".concat(seat, ":"), error);
@@ -396,12 +401,15 @@ const SeatingPlan = _ref => {
396
401
  try {
397
402
  for (let seat of seatsToRemove) {
398
403
  try {
399
- await fetch("".concat(apiUrl, "/seat/").concat(seat.ssId), {
404
+ const res = await fetch("".concat(apiUrl, "/seat/").concat(seat.ssId), {
400
405
  method: "DELETE",
401
406
  headers: {
402
407
  "basket-key": sessionId
403
408
  }
404
409
  });
410
+ if (!res.ok) {
411
+ throw new Error("".concat(res.status));
412
+ }
405
413
  succeededIds.push(seat.ssId);
406
414
  } catch (error) {
407
415
  console.error("Failed to remove seat ".concat(seat, ":"), error);
@@ -580,10 +588,6 @@ const SeatingPlan = _ref => {
580
588
  }
581
589
  return acc;
582
590
  }, {});
583
- const cancelPriceSelect = () => {
584
- setPricingPopupOpen(false);
585
- setSelectedSeats([]);
586
- };
587
591
 
588
592
  // return seating plan
589
593
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
@@ -598,27 +602,32 @@ const SeatingPlan = _ref => {
598
602
  className: "seating-plan-container ".concat(isFullScreen ? "full-screen" : ""),
599
603
  "data-mode": mode
600
604
  }, pricingPopupOpen && (canMultiSelect || seats.preventOrphanedSeats) ? /*#__PURE__*/_react.default.createElement(_PricingPopup.default, {
601
- cancel: cancelPriceSelect,
605
+ onClose: () => {
606
+ setPricingPopupOpen(false);
607
+ setSelectedSeats([]);
608
+ },
602
609
  groupedSelectedSeats: groupedSelectedSeats,
603
610
  selectedSeatsWithPricing: selectedSeatsWithPricing,
604
611
  priceSectionIds: priceSectionIds,
605
612
  pricing: seats.pricing,
606
613
  batchAddTicketsToCart: batchAddTicketsToCart,
607
- singleSeats: seats.preventOrphanedSeats
608
- }) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null), selectQuantityPopupOpen && /*#__PURE__*/_react.default.createElement(_SelectQuantityPopup.default, {
614
+ isSingleSeats: seats.preventOrphanedSeats
615
+ }) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null), seats.preventOrphanedSeats && selectQuantityPopupOpen ? /*#__PURE__*/_react.default.createElement(_SelectQuantityPopup.default, {
609
616
  quantity: desiredSeatQuantity,
610
617
  setQuantity: v => {
611
618
  setDesiredSeatQuantity(v);
612
619
  setSelectedSeats([]);
613
620
  },
614
621
  onClose: () => setSelectQuantityPopupOpen(false)
615
- }), removeMultipleSeatsPopupOpen && /*#__PURE__*/_react.default.createElement(_RemoveMultipleSeatsPopup.default, {
622
+ }) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null), seats.preventOrphanedSeats && removeMultipleSeatsPopupOpen ? /*#__PURE__*/_react.default.createElement(_RemoveMultipleSeatsPopup.default, {
616
623
  onConfirm: () => {
617
624
  batchRemoveTicketsFromCart(seatsToRemove);
618
625
  setRemoveMultipleSeatsPopupOpen(false);
619
626
  },
620
627
  onClose: () => setRemoveMultipleSeatsPopupOpen(false)
621
- }), /*#__PURE__*/_react.default.createElement(_reactLeaflet.MapContainer, {
628
+ }) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null), seats.preventOrphanedSeats && invalidSeatsPopupOpen ? /*#__PURE__*/_react.default.createElement(_InvalidSeatsPopup.default, {
629
+ onClose: () => setInvalidSeatsPopupOpen(false)
630
+ }) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null), /*#__PURE__*/_react.default.createElement(_reactLeaflet.MapContainer, {
622
631
  zoomSnap: 0,
623
632
  zoomDelta: 1,
624
633
  center: (0, _utils.calculateCenterOfMap)(height, seats.seats),
@@ -660,7 +669,8 @@ const SeatingPlan = _ref => {
660
669
  setSelectQuantityPopupOpen: setSelectQuantityPopupOpen,
661
670
  desiredSeatQuantity: desiredSeatQuantity,
662
671
  setSeatsToRemove: setSeatsToRemove,
663
- setRemoveMultipleSeatsPopupOpen: setRemoveMultipleSeatsPopupOpen
672
+ setRemoveMultipleSeatsPopupOpen: setRemoveMultipleSeatsPopupOpen,
673
+ setInvalidSeatsPopupOpen: setInvalidSeatsPopupOpen
664
674
  }))), mapContainer)) : /*#__PURE__*/_react.default.createElement("div", {
665
675
  className: "loading"
666
676
  }, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("svg", {
@@ -37,10 +37,10 @@ function SelectQuantityPopup(_ref2) {
37
37
  } = _ref2;
38
38
  const [newQuantity, setNewQuantity] = (0, _react.useState)(quantity);
39
39
  return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
40
- className: "pricing-popup-backdrop",
40
+ className: "seatingplan-dialog-backdrop",
41
41
  onClick: onClose
42
42
  }), /*#__PURE__*/_react.default.createElement("div", {
43
- className: "pricing-popup",
43
+ className: "seatingplan-dialog",
44
44
  role: "dialog",
45
45
  "aria-modal": "true"
46
46
  }, /*#__PURE__*/_react.default.createElement("div", {
@@ -59,7 +59,7 @@ function SelectQuantityPopup(_ref2) {
59
59
  height: "20px",
60
60
  width: "20px"
61
61
  })), /*#__PURE__*/_react.default.createElement("h3", null, "Choose seats")), /*#__PURE__*/_react.default.createElement("div", {
62
- className: "pricing-popup-content"
62
+ className: "seatingplan-dialog-content"
63
63
  }, /*#__PURE__*/_react.default.createElement("h4", {
64
64
  className: "quantity-heading"
65
65
  }, "How many seats would you like to book?"), /*#__PURE__*/_react.default.createElement("div", {
@@ -1098,6 +1098,210 @@ svg.leaflet-image-layer.leaflet-interactive path {
1098
1098
  }
1099
1099
  }
1100
1100
 
1101
+ .seatingplan-dialog-backdrop {
1102
+ position: absolute;
1103
+ width: 100%;
1104
+ height: 100%;
1105
+ top: 0;
1106
+ left: 0;
1107
+ background-color: rgba(0, 0, 0, 0.4);
1108
+ z-index: 1001;
1109
+ animation: fade-in 400ms;
1110
+ }
1111
+
1112
+ .seatingplan-dialog-content {
1113
+ padding: 1rem;
1114
+ flex: 1;
1115
+ overflow-y: auto;
1116
+ }
1117
+
1118
+ .seatingplan-dialog {
1119
+ position: absolute;
1120
+ z-index: 1002;
1121
+ top: 50%;
1122
+ left: 50%;
1123
+ transform: translate(-50%, -50%);
1124
+ animation: fade-in 400ms;
1125
+ background-color: #fff;
1126
+ /* font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; */
1127
+ font-family: inherit;
1128
+ color: #333;
1129
+ box-shadow: 0 3px 14px rgba(0, 0, 0, 0.4);
1130
+ border-radius: 12px;
1131
+ min-width: 20rem;
1132
+ max-height: 95%;
1133
+ display: flex;
1134
+ flex-direction: column;
1135
+ }
1136
+
1137
+ @media screen and (max-width: 600px) {
1138
+ .seatingplan-dialog {
1139
+ width: 100%;
1140
+ min-width: unset;
1141
+ }
1142
+ }
1143
+
1144
+ .seatingplan-dialog.remove-multiple-seats-popup {
1145
+ min-width: unset;
1146
+ max-width: 28rem;
1147
+ }
1148
+
1149
+ .seatingplan-dialog .popup-header {
1150
+ margin-top: 0;
1151
+ padding: 1rem;
1152
+ border-radius: 12px 12px 0px 0px;
1153
+ flex-direction: column;
1154
+ text-align: left;
1155
+ gap: 0.5rem;
1156
+ }
1157
+
1158
+ .seatingplan-dialog .popup-header .close-button {
1159
+ margin: -2px -2px 0px 0px;
1160
+ align-self: flex-end;
1161
+ }
1162
+
1163
+ .seatingplan-dialog h3,
1164
+ .seatingplan-dialog h4 {
1165
+ font-weight: bold;
1166
+ margin: 0;
1167
+ }
1168
+
1169
+ .seatingplan-dialog .quantity-heading {
1170
+ font-weight: normal;
1171
+ text-align: left;
1172
+ font-size: 0.875rem;
1173
+ margin-bottom: 8px;
1174
+ }
1175
+
1176
+ .seatingplan-dialog p {
1177
+ margin: 0;
1178
+ max-width: 30rem;
1179
+ }
1180
+
1181
+ .seatingplan-dialog .seat-names {
1182
+ font-size: 0.9rem;
1183
+ }
1184
+
1185
+ .seatingplan-dialog .seat-list {
1186
+ text-align: left;
1187
+ display: flex;
1188
+ flex-direction: column;
1189
+ gap: 1rem;
1190
+ }
1191
+
1192
+ .seatingplan-dialog .seat-group {
1193
+ display: flex;
1194
+ flex-direction: column;
1195
+ gap: 0.5rem;
1196
+ }
1197
+
1198
+ .seatingplan-dialog .seat-group-header {
1199
+ display: flex;
1200
+ justify-content: space-between;
1201
+ align-items: flex-end;
1202
+ gap: 1rem;
1203
+ font-size: 0.9rem;
1204
+ }
1205
+
1206
+ .seatingplan-dialog select {
1207
+ flex: 1;
1208
+ padding: 0.3rem 0.2rem;
1209
+ border: 1px solid #ccc;
1210
+ border-radius: 0.25rem;
1211
+ }
1212
+
1213
+ .seatingplan-dialog .buttons {
1214
+ display: flex;
1215
+ width: 100%;
1216
+ justify-content: space-between;
1217
+ gap: 1rem;
1218
+ margin-top: 1.2rem;
1219
+ }
1220
+
1221
+ .seatingplan-dialog .buttons.buttons-single {
1222
+ justify-content: center;
1223
+ }
1224
+
1225
+ .seatingplan-dialog .buttons button {
1226
+ display: block;
1227
+ background: transparent;
1228
+ min-width: 12rem;
1229
+ border-radius: 0.25rem;
1230
+ padding: 0.5rem;
1231
+ border: 1px solid #333;
1232
+ margin-bottom: 0.2rem;
1233
+ cursor: pointer;
1234
+ margin-bottom: 0;
1235
+ }
1236
+
1237
+ .seatingplan-dialog .buttons.buttons-single button {
1238
+ width: 100%;
1239
+ }
1240
+
1241
+ .seatingplan-dialog .buttons button.add-cart {
1242
+ background: var(--accent);
1243
+ color: white;
1244
+ font-weight: bold;
1245
+ border: none;
1246
+ }
1247
+
1248
+ .seatingplan-dialog .buttons button.add-cart:hover:not(:disabled) {
1249
+ background: var(--accent-light);
1250
+ color: var(--accent-dark);
1251
+ }
1252
+
1253
+ @media screen and (max-width: 600px) {
1254
+ .seatingplan-dialog .buttons button {
1255
+ min-width: 8rem;
1256
+ }
1257
+ }
1258
+
1259
+ .seatingplan-dialog .buttons button:disabled {
1260
+ cursor: default;
1261
+ color: #bdc3c7;
1262
+ border-color: #bdc3c7;
1263
+ }
1264
+
1265
+ .seatingplan-dialog .buttons button:hover:not(:disabled) {
1266
+ background: #ecf0f1;
1267
+ transition: 0.2s;
1268
+ }
1269
+
1270
+ .seatingplan-dialog .no-prices-available {
1271
+ font-size: 0.8rem;
1272
+ color: red;
1273
+ }
1274
+
1275
+ .seatingplan-dialog button.cancel {
1276
+ background: #dcdcdc;
1277
+ border: none;
1278
+ }
1279
+
1280
+ .seatingplan-dialog .price-select {
1281
+ display: flex;
1282
+ gap: 8px;
1283
+ }
1284
+
1285
+ .seatingplan-dialog .flexi-button {
1286
+ width: max-content;
1287
+ display: flex;
1288
+ align-items: center;
1289
+ gap: 4px;
1290
+ border-radius: 0.25rem;
1291
+ padding: 0.5rem;
1292
+ border: none;
1293
+ text-decoration: underline;
1294
+ background: transparent;
1295
+ cursor: pointer;
1296
+ animation: fade-in 200ms;
1297
+ font-weight: 600;
1298
+ transition: 0.2s;
1299
+ }
1300
+
1301
+ .flexi-button:hover {
1302
+ background: #ecf0f1;
1303
+ }
1304
+
1101
1305
  /* Printing */
1102
1306
 
1103
1307
  @media print {
@@ -1,201 +1,3 @@
1
- /* Popup to select prices */
2
-
3
- .pricing-popup-backdrop {
4
- position: absolute;
5
- width: 100%;
6
- height: 100%;
7
- top: 0;
8
- left: 0;
9
- background-color: rgba(0, 0, 0, 0.4);
10
- z-index: 1001;
11
- animation: fade-in 400ms;
12
- }
13
-
14
- .pricing-popup-content {
15
- padding: 1rem;
16
- flex: 1;
17
- overflow-y: auto;
18
- }
19
-
20
- .pricing-popup {
21
- position: absolute;
22
- z-index: 1002;
23
- top: 50%;
24
- left: 50%;
25
- transform: translate(-50%, -50%);
26
- animation: fade-in 400ms;
27
- background-color: #fff;
28
- /* font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; */
29
- font-family: inherit;
30
- color: #333;
31
- box-shadow: 0 3px 14px rgba(0, 0, 0, 0.4);
32
- border-radius: 12px;
33
- min-width: 20rem;
34
- max-height: 95%;
35
- display: flex;
36
- flex-direction: column;
37
- }
38
-
39
- @media screen and (max-width: 600px) {
40
- .pricing-popup {
41
- width: 100%;
42
- min-width: unset;
43
- }
44
- }
45
-
46
- .pricing-popup.remove-multiple-seats-popup {
47
- min-width: unset;
48
- max-width: 28rem;
49
- }
50
-
51
- .pricing-popup .popup-header {
52
- margin-top: 0;
53
- padding: 1rem;
54
- border-radius: 12px 12px 0px 0px;
55
- flex-direction: column;
56
- text-align: left;
57
- gap: 0.5rem;
58
- }
59
-
60
- .pricing-popup .popup-header .close-button {
61
- margin: -2px -2px 0px 0px;
62
- align-self: flex-end;
63
- }
64
-
65
- .pricing-popup h3,
66
- .pricing-popup h4 {
67
- font-weight: bold;
68
- margin: 0;
69
- }
70
-
71
- .pricing-popup .quantity-heading {
72
- font-weight: normal;
73
- text-align: left;
74
- font-size: 0.875rem;
75
- margin-bottom: 8px;
76
- }
77
-
78
- .pricing-popup p {
79
- margin: 0;
80
- max-width: 30rem;
81
- }
82
-
83
- .pricing-popup .seat-names {
84
- font-size: 0.9rem;
85
- }
86
-
87
- .pricing-popup .seat-list {
88
- text-align: left;
89
- display: flex;
90
- flex-direction: column;
91
- gap: 1rem;
92
- }
93
-
94
- .pricing-popup .seat-group {
95
- display: flex;
96
- flex-direction: column;
97
- gap: 0.5rem;
98
- }
99
-
100
- .pricing-popup .seat-group-header {
101
- display: flex;
102
- justify-content: space-between;
103
- align-items: flex-end;
104
- gap: 1rem;
105
- font-size: 0.9rem;
106
- }
107
-
108
- .pricing-popup select {
109
- flex: 1;
110
- padding: 0.3rem 0.2rem;
111
- border: 1px solid #ccc;
112
- border-radius: 0.25rem;
113
- }
114
-
115
- .pricing-popup .buttons {
116
- display: flex;
117
- width: 100%;
118
- justify-content: space-between;
119
- gap: 1rem;
120
- margin-top: 1.2rem;
121
- }
122
-
123
- .pricing-popup .buttons button {
124
- display: block;
125
- background: transparent;
126
- min-width: 12rem;
127
- border-radius: 0.25rem;
128
- padding: 0.5rem;
129
- border: 1px solid #333;
130
- margin-bottom: 0.2rem;
131
- cursor: pointer;
132
- margin-bottom: 0;
133
- }
134
-
135
- .pricing-popup .buttons button.add-cart {
136
- background: var(--accent);
137
- color: white;
138
- font-weight: bold;
139
- border: none;
140
- }
141
-
142
- .pricing-popup .buttons button.add-cart:hover:not(:disabled) {
143
- background: var(--accent-light);
144
- color: var(--accent-dark);
145
- }
146
-
147
- @media screen and (max-width: 600px) {
148
- .pricing-popup .buttons button {
149
- min-width: 8rem;
150
- }
151
- }
152
-
153
- .pricing-popup .buttons button:disabled {
154
- cursor: default;
155
- color: #bdc3c7;
156
- border-color: #bdc3c7;
157
- }
158
-
159
- .pricing-popup .buttons button:hover:not(:disabled) {
160
- background: #ecf0f1;
161
- transition: 0.2s;
162
- }
163
-
164
- .pricing-popup .no-prices-available {
165
- font-size: 0.8rem;
166
- color: red;
167
- }
168
-
169
- .pricing-popup button.cancel {
170
- background: #dcdcdc;
171
- border: none;
172
- }
173
-
174
- .pricing-popup .price-select {
175
- display: flex;
176
- gap: 8px;
177
- }
178
-
179
- .pricing-popup .flexi-button {
180
- width: max-content;
181
- display: flex;
182
- align-items: center;
183
- gap: 4px;
184
- border-radius: 0.25rem;
185
- padding: 0.5rem;
186
- border: none;
187
- text-decoration: underline;
188
- background: transparent;
189
- cursor: pointer;
190
- animation: fade-in 200ms;
191
- font-weight: 600;
192
- transition: 0.2s;
193
- }
194
-
195
- .flexi-button:hover {
196
- background: #ecf0f1;
197
- }
198
-
199
1
  /* Button controls */
200
2
 
201
3
  .leaflet-draw.leaflet-control {
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.statuses = exports.statusColors = exports.modes = exports.bookingModes = void 0;
7
7
  const statusColors = exports.statusColors = {
8
- unknown: "#95a5a6",
8
+ unknown: "#bdc3c7",
9
9
  available: "#00E640",
10
10
  sold: "#95a5a6",
11
11
  reserved: "#95a5a6",
@@ -3,15 +3,32 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.getValidSeats = exports.getRowSection = exports.getInitialColor = exports.getAdjacentBookedSeats = exports.createsOrphan = exports.canSelectSingleSeatGivenOrphanRules = exports.calculateCenterOfMap = exports.NZDollar = void 0;
6
+ exports.isTouchScreen = exports.getValidSeats = exports.getRowSection = exports.getInitialColor = exports.getAdjacentBookedSeats = exports.createsOrphan = exports.canSelectSingleSeatGivenOrphanRules = exports.calculateCenterOfMap = exports.NZDollar = void 0;
7
7
  var _enums = require("./enums");
8
+ const isTouchScreen = () => {
9
+ let hasTouchScreen = false;
10
+ if (typeof window !== "undefined") {
11
+ if ("maxTouchPoints" in window.navigator) {
12
+ hasTouchScreen = window.navigator.maxTouchPoints > 0;
13
+ } else {
14
+ var _matchMedia;
15
+ const mQ = (_matchMedia = matchMedia) === null || _matchMedia === void 0 ? void 0 : _matchMedia("(pointer:coarse)");
16
+ if ((mQ === null || mQ === void 0 ? void 0 : mQ.media) === "(pointer:coarse)") {
17
+ hasTouchScreen = !!mQ.matches;
18
+ }
19
+ }
20
+ }
21
+ return hasTouchScreen;
22
+ };
23
+ exports.isTouchScreen = isTouchScreen;
8
24
  const getInitialColor = (s, price, disabled, selected, greyedOut) => {
9
25
  // selected state for multiselect
10
26
  if (selected) {
11
27
  return {
12
28
  fillColor: _enums.statusColors.selected,
13
29
  color: "none",
14
- fillOpacity: s.loading ? 0 : 1
30
+ fillOpacity: s.loading ? 0 : 1,
31
+ stroke: false
15
32
  };
16
33
  }
17
34
  if (greyedOut) {
@@ -94,55 +111,88 @@ const canSelectSingleSeatGivenOrphanRules = (seat, rowSection) => {
94
111
  return true;
95
112
  };
96
113
  exports.canSelectSingleSeatGivenOrphanRules = canSelectSingleSeatGivenOrphanRules;
114
+ const traverse = (arr, seat, direction, rowSectionMap, desiredSeatQuantity, rowSection, ignoreOrphans) => {
115
+ const nextSeatId = direction === "left" ? seat.sb : seat.sa;
116
+ if (nextSeatId) {
117
+ const nextSeat = rowSectionMap.get(nextSeatId);
118
+ if (nextSeat && nextSeat.s === _enums.statuses.UNSOLD && arr.length < desiredSeatQuantity && !(!ignoreOrphans && arr.length === desiredSeatQuantity - 1 && createsOrphan(nextSeat, rowSection, false, arr)) && !arr.some(s => s.sId === nextSeat.sId)) {
119
+ if (direction === "left") {
120
+ arr.unshift(nextSeat);
121
+ } else {
122
+ arr.push(nextSeat);
123
+ }
124
+ if (arr.length < desiredSeatQuantity) {
125
+ traverse(arr, nextSeat, direction, rowSectionMap, desiredSeatQuantity, rowSection, ignoreOrphans);
126
+ }
127
+ }
128
+ }
129
+ };
130
+ const getSeats = (seat, rowSectionMap, desiredSeatQuantity, rowSection, ignoreOrphans) => {
131
+ let seats = [seat];
132
+ traverse(seats, seat, "right", rowSectionMap, desiredSeatQuantity, rowSection, ignoreOrphans);
133
+ if (seats.length < desiredSeatQuantity) {
134
+ traverse(seats, seat, "left", rowSectionMap, desiredSeatQuantity, rowSection, ignoreOrphans);
135
+ }
136
+ const firstSeat = seats[0];
137
+ const sb = rowSectionMap.get(firstSeat.sb);
138
+ const sbb = rowSectionMap.get(sb === null || sb === void 0 ? void 0 : sb.sb);
139
+ const orphanToLeft = sb && sbb && sb.s === _enums.statuses.UNSOLD && sbb.s !== _enums.statuses.UNSOLD || sb && !sbb && sb.s === _enums.statuses.UNSOLD;
140
+ if (seats.length < desiredSeatQuantity || orphanToLeft && !ignoreOrphans) {
141
+ seats = [seat];
142
+ traverse(seats, seat, "left", rowSectionMap, desiredSeatQuantity, rowSection, ignoreOrphans);
143
+ if (seats.length < desiredSeatQuantity) {
144
+ traverse(seats, seat, "right", rowSectionMap, desiredSeatQuantity, rowSection, ignoreOrphans);
145
+ }
146
+ }
147
+ const lastSeat = seats[seats.length - 1];
148
+ const sa = rowSectionMap.get(lastSeat.sa);
149
+ const saa = rowSectionMap.get(sa === null || sa === void 0 ? void 0 : sa.sa);
150
+ const orphanToRight = sa && saa && sa.s === _enums.statuses.UNSOLD && saa.s !== _enums.statuses.UNSOLD || sa && !saa && sa.s === _enums.statuses.UNSOLD;
151
+ if (orphanToRight && !ignoreOrphans) {
152
+ return [];
153
+ }
154
+ return seats;
155
+ };
97
156
  const getValidSeats = (s, seatsMap, desiredSeatQuantity) => {
98
- let seatsToBook = [s];
99
157
  const rowSection = getRowSection(s, seatsMap);
158
+ const rowSectionMap = new Map(rowSection.map(seat => [seat.sId, seat]));
159
+ if (s && desiredSeatQuantity === 1) {
160
+ return {
161
+ seats: [s],
162
+ valid: canSelectSingleSeatGivenOrphanRules(s, rowSection) ? true : false
163
+ };
164
+ }
165
+ const seatsToBook = getSeats(s, rowSectionMap, desiredSeatQuantity, rowSection);
100
166
  if (seatsToBook.length < desiredSeatQuantity) {
101
- const traverse = (seat, direction) => {
102
- const nextSeatId = direction === "left" ? seat.sb : seat.sa;
103
- if (nextSeatId) {
104
- const nextSeat = rowSection.find(v => v.sId === nextSeatId);
105
- if (nextSeat && nextSeat.s === _enums.statuses.UNSOLD && seatsToBook.length < desiredSeatQuantity && !(seatsToBook.length === desiredSeatQuantity - 1 && createsOrphan(nextSeat, rowSection, false, seatsToBook))) {
106
- if (direction === "left") {
107
- seatsToBook.unshift(nextSeat);
108
- } else {
109
- seatsToBook.push(nextSeat);
110
- }
111
- if (seatsToBook.length < desiredSeatQuantity) {
112
- traverse(nextSeat, direction);
167
+ let hasOtherOptions = false;
168
+ const orphaningSeats = getSeats(s, rowSectionMap, desiredSeatQuantity, rowSection, true);
169
+ if (orphaningSeats.length === desiredSeatQuantity) {
170
+ const otherAvailableSeats = rowSection.filter(seat => seat.s === _enums.statuses.UNSOLD && seat.sId !== s.sId);
171
+ if (otherAvailableSeats.length > 0) {
172
+ for (const seat of otherAvailableSeats) {
173
+ const possibleSeats = getSeats(seat, rowSectionMap, desiredSeatQuantity, rowSection);
174
+ if (possibleSeats.length === desiredSeatQuantity) {
175
+ hasOtherOptions = true;
176
+ break;
113
177
  }
114
178
  }
115
179
  }
116
- };
117
- traverse(s, "right");
118
- if (seatsToBook.length < desiredSeatQuantity) {
119
- traverse(s, "left");
180
+ return {
181
+ seats: orphaningSeats,
182
+ valid: hasOtherOptions ? false : true
183
+ };
120
184
  }
121
- const firstSeat = seatsToBook[0];
122
- const sb = rowSection.find(v => v.sId === firstSeat.sb);
123
- const sbb = rowSection.find(v => v.sId === (sb === null || sb === void 0 ? void 0 : sb.sb));
124
- const orphanToLeft = sb && sbb && sb.s === _enums.statuses.UNSOLD && sbb.s !== _enums.statuses.UNSOLD || sb && !sbb && sb.s === _enums.statuses.UNSOLD;
125
- if (seatsToBook.length < desiredSeatQuantity || orphanToLeft) {
126
- seatsToBook = [s];
127
- traverse(s, "left");
128
- if (seatsToBook.length < desiredSeatQuantity) {
129
- traverse(s, "right");
130
- }
131
- }
132
- }
133
- if (seatsToBook.length === 1 && desiredSeatQuantity === 1) {
134
- return canSelectSingleSeatGivenOrphanRules(s, rowSection) ? seatsToBook : [];
135
185
  }
136
- if (seatsToBook.length === desiredSeatQuantity && !(seatsToBook.length === 1 && createsOrphan(s, rowSection, false, seatsToBook))) {
137
- return seatsToBook;
138
- }
139
- return [];
186
+ return {
187
+ seats: seatsToBook,
188
+ valid: seatsToBook.length === desiredSeatQuantity ? true : false
189
+ };
140
190
  };
141
191
  exports.getValidSeats = getValidSeats;
142
192
  const getAdjacentBookedSeats = (seat, seatsMap) => {
143
193
  const rowSection = getRowSection(seat, seatsMap);
144
194
  const adjacentBookedSeats = [seat];
145
- const traverse = (seat, direction) => {
195
+ const traverseRow = (seat, direction) => {
146
196
  const nextSeatId = direction === "left" ? seat.sb : seat.sa;
147
197
  if (nextSeatId) {
148
198
  const nextSeat = rowSection.find(v => v.sId === nextSeatId);
@@ -152,12 +202,12 @@ const getAdjacentBookedSeats = (seat, seatsMap) => {
152
202
  } else {
153
203
  adjacentBookedSeats.push(nextSeat);
154
204
  }
155
- traverse(nextSeat, direction);
205
+ traverseRow(nextSeat, direction);
156
206
  }
157
207
  }
158
208
  };
159
- traverse(seat, "right");
160
- traverse(seat, "left");
209
+ traverseRow(seat, "right");
210
+ traverseRow(seat, "left");
161
211
  return adjacentBookedSeats;
162
212
  };
163
213
  exports.getAdjacentBookedSeats = getAdjacentBookedSeats;
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": "1.6.9",
5
+ "version": "1.7.1",
6
6
  "private": false,
7
7
  "keywords": [
8
8
  "iticket",