iticket-seatingplan-dev 1.8.2 → 1.8.3

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.
@@ -68,6 +68,7 @@ const SeatingPlan = _ref => {
68
68
  const [seatsToRemove, setSeatsToRemove] = (0, _react.useState)([]);
69
69
  const [removeMultipleSeatsPopupOpen, setRemoveMultipleSeatsPopupOpen] = (0, _react.useState)(false);
70
70
  const [invalidSeatsPopupOpen, setInvalidSeatsPopupOpen] = (0, _react.useState)(false);
71
+ const [retrying, setRetrying] = (0, _react.useState)([]);
71
72
  const mapRef = (0, _react.useRef)(null);
72
73
  const canMultiSelect = bookingMode === _utils.bookingModes.POS;
73
74
  const apiUrl = "".concat(baseUrl, "/legacy/").concat(countryCode, "/shop/events/").concat(eventId, "/").concat(eventVenueId, "/showings/").concat(showingId, "/tickets/allocated/").concat(areaId);
@@ -84,18 +85,22 @@ const SeatingPlan = _ref => {
84
85
  };
85
86
  callbackFunction === null || callbackFunction === void 0 || callbackFunction(event);
86
87
  };
88
+ const updateSeat = (seatIndex, newSeat) => {
89
+ if (seatIndex !== -1) {
90
+ setSeats(prev => {
91
+ const updatedSeats = _objectSpread({}, prev);
92
+ updatedSeats.seats[seatIndex] = newSeat;
93
+ return updatedSeats;
94
+ });
95
+ }
96
+ };
87
97
  const _addTicketToCart = async function addTicketToCart(s, priceage) {
88
98
  var _mapRef$current;
89
99
  let retryCount = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
90
- const updatedSeats = _objectSpread({}, seats);
91
- const seatIndex = updatedSeats.seats.findIndex(seat => seat.ssId === s.ssId);
92
- const setSeatLoading = loading => {
93
- if (seatIndex !== -1) {
94
- updatedSeats.seats[seatIndex].loading = loading;
95
- setSeats(updatedSeats);
96
- }
97
- };
98
- setSeatLoading(true);
100
+ const seatIndex = seats.seats.findIndex(seat => seat.ssId === s.ssId);
101
+ updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
102
+ loading: true
103
+ }));
99
104
  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);
100
105
  if (!isSeatAvailable) {
101
106
  if (bookedSeats.length >= quantity) {
@@ -107,7 +112,9 @@ const SeatingPlan = _ref => {
107
112
  message: _utils.ERROR_MESSAGES.NO_ALLOCATION
108
113
  });
109
114
  }
110
- setSeatLoading(false);
115
+ updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
116
+ loading: false
117
+ }));
111
118
  return;
112
119
  }
113
120
  (_mapRef$current = mapRef.current) === null || _mapRef$current === void 0 || (_mapRef$current = _mapRef$current.target) === null || _mapRef$current === void 0 || _mapRef$current.closePopup();
@@ -125,44 +132,50 @@ const SeatingPlan = _ref => {
125
132
  "basket-key": sessionId
126
133
  }
127
134
  });
128
- if ((response === null || response === void 0 ? void 0 : response.status) === 403 || (response === null || response === void 0 ? void 0 : response.status) === 400) {
129
- if (seatIndex !== -1) {
130
- updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, s), {}, {
131
- loading: false,
132
- s: _utils.statuses.SOLD
133
- });
134
- setSeats(updatedSeats);
135
- }
136
- handleError({
137
- code: 403,
138
- message: response.status === 400 ? _utils.ERROR_MESSAGES.INCOMPATIBLE_TICKETS : _utils.ERROR_MESSAGES.SEAT_UNAVAILABLE
139
- });
140
- } else if ((response === null || response === void 0 ? void 0 : response.status) === 429) {
135
+ if ((response === null || response === void 0 ? void 0 : response.status) === 429) {
141
136
  if (retryCount < BOOKING_MAX_RETRIES) {
137
+ setRetrying(prev => [...prev, s.ssId]);
142
138
  const delay = (0, _utils.getRetryDelay)(response, retryCount);
143
139
  await new Promise(resolve => setTimeout(resolve, delay));
144
140
  return _addTicketToCart(s, priceage, retryCount + 1);
145
141
  }
146
- setSeatLoading(false);
142
+ updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
143
+ loading: false
144
+ }));
145
+ setRetrying(prev => prev.filter(id => id !== s.ssId));
147
146
  handleError({
148
147
  code: 429,
148
+ message: _utils.ERROR_MESSAGES.MAX_RETRIES
149
+ });
150
+ return;
151
+ }
152
+ setRetrying(prev => prev.filter(id => id !== s.ssId));
153
+ if ((response === null || response === void 0 ? void 0 : response.status) === 403) {
154
+ updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
155
+ loading: false,
156
+ s: _utils.statuses.SOLD
157
+ }));
158
+ handleError({
159
+ code: 403,
149
160
  message: _utils.ERROR_MESSAGES.SEAT_TAKEN
150
161
  });
151
- } else if (!response.ok) {
152
- setSeatLoading(false);
162
+ return;
163
+ }
164
+ updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
165
+ loading: false
166
+ }));
167
+ if (!(response !== null && response !== void 0 && response.ok)) {
153
168
  handleError({
154
- code: 500,
155
- message: _utils.ERROR_MESSAGES.GENERIC_ERROR
169
+ code: (response === null || response === void 0 ? void 0 : response.status) === 400 ? 400 : 500,
170
+ message: (response === null || response === void 0 ? void 0 : response.status) === 400 ? _utils.ERROR_MESSAGES.INCOMPATIBLE_TICKETS : _utils.ERROR_MESSAGES.GENERIC_ERROR
156
171
  });
157
172
  } else {
158
- if (seatIndex !== -1) {
159
- updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, s), {}, {
160
- s: _utils.statuses.USER_PENDING,
161
- bookedPrice: price,
162
- loading: false
163
- });
164
- setSeats(updatedSeats);
165
- }
173
+ setRetrying(prev => prev.filter(id => id !== s.ssId));
174
+ updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
175
+ loading: false,
176
+ s: _utils.statuses.USER_PENDING,
177
+ bookedPrice: price
178
+ }));
166
179
  setBookedSeats(prev => [...prev, {
167
180
  ssId: s.ssId,
168
181
  r: s.r,
@@ -187,38 +200,32 @@ const SeatingPlan = _ref => {
187
200
  callbackFunction === null || callbackFunction === void 0 || callbackFunction(event);
188
201
  }
189
202
  } catch (_unused) {
190
- setSeatLoading(false);
203
+ updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
204
+ loading: false
205
+ }));
206
+ setRetrying(prev => prev.filter(id => id !== s.ssId));
191
207
  handleError({
192
208
  code: 500,
193
209
  message: _utils.ERROR_MESSAGES.GENERIC_ERROR
194
210
  });
195
211
  }
196
- _initialFetch(true);
197
212
  };
198
213
  const removeTicketFromCart = (s, e) => {
199
- const updatedSeats = _objectSpread({}, seats);
200
- const seatIndex = updatedSeats.seats.findIndex(seat => seat.ssId === s.ssId);
201
- const setSeatLoading = loading => {
202
- if (seatIndex !== -1) {
203
- updatedSeats.seats[seatIndex].loading = loading;
204
- setSeats(updatedSeats);
205
- }
206
- };
207
- setSeatLoading(true);
214
+ const seatIndex = seats.seats.findIndex(seat => seat.ssId === s.ssId);
215
+ updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
216
+ loading: true
217
+ }));
208
218
  fetch("".concat(apiUrl, "/seat/").concat(s.ssId), {
209
219
  method: "DELETE",
210
220
  headers: {
211
221
  "basket-key": sessionId
212
222
  }
213
223
  }).then(() => {
214
- if (seatIndex !== -1) {
215
- updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, s), {}, {
216
- loading: false,
217
- s: _utils.statuses.UNSOLD,
218
- bookedPrice: null
219
- });
220
- setSeats(updatedSeats);
221
- }
224
+ updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
225
+ loading: false,
226
+ s: _utils.statuses.UNSOLD,
227
+ bookedPrice: null
228
+ }));
222
229
  setBookedSeats(prev => prev.filter(bs => bs.ssId !== s.ssId));
223
230
  const event = {
224
231
  type: "cart-change-remove",
@@ -226,31 +233,35 @@ const SeatingPlan = _ref => {
226
233
  };
227
234
  callbackFunction === null || callbackFunction === void 0 || callbackFunction(event);
228
235
  }).catch(() => {
229
- setSeatLoading(false);
230
- e.target.setStyle({
231
- fillColor: _utils.statusColors.booked
232
- });
236
+ updateSeat(seatIndex, _objectSpread(_objectSpread({}, s), {}, {
237
+ loading: false
238
+ }));
233
239
  handleError({
234
240
  code: 500,
235
241
  message: _utils.ERROR_MESSAGES.GENERIC_ERROR
236
242
  });
237
- }).finally(() => {
238
- _initialFetch(true);
239
243
  });
240
244
  };
241
245
  const batchAddTicketsToCart = async seatsToBook => {
242
246
  if (!canMultiSelect && !seats.preventOrphanedSeats || seatsToBook.length === 0) {
243
247
  return;
244
248
  }
245
- const updatedSeats = _objectSpread({}, seats);
246
- const seatIds = seatsToBook.map(s => s.seat.ssId);
247
249
  const setSeatsLoading = loading => {
248
- updatedSeats.seats.forEach(s => {
249
- if (seatIds.includes(s.ssId)) {
250
- s.loading = loading;
251
- }
250
+ setSeats(prev => {
251
+ const updatedSeats = _objectSpread({}, prev);
252
+ seatsToBook.forEach(_ref2 => {
253
+ let {
254
+ seat
255
+ } = _ref2;
256
+ const seatIndex = updatedSeats.seats.findIndex(s => s.ssId === seat.ssId);
257
+ if (seatIndex !== -1) {
258
+ updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, updatedSeats.seats[seatIndex]), {}, {
259
+ loading: loading
260
+ });
261
+ }
262
+ });
263
+ return updatedSeats;
252
264
  });
253
- setSeats(updatedSeats);
254
265
  };
255
266
  setSeatsLoading(true);
256
267
  const succeeded = [];
@@ -272,27 +283,31 @@ const SeatingPlan = _ref => {
272
283
  });
273
284
  if (res.status === 429) {
274
285
  if (retryCount < BOOKING_MAX_RETRIES) {
286
+ setRetrying(prev => [...prev, seatToBook.seat.ssId]);
275
287
  const delay = (0, _utils.getRetryDelay)(res, retryCount);
276
288
  await new Promise(resolve => setTimeout(resolve, delay));
277
289
  return _tryBook(seatToBook, retryCount + 1);
290
+ } else {
291
+ throw new Error("429");
278
292
  }
279
- throw new Error("429");
280
- }
281
- if (!res.ok) {
293
+ } else if (!res.ok) {
282
294
  throw new Error("".concat(res.status));
283
295
  }
284
- succeeded.push(seatToBook);
296
+ succeeded.push(seatToBook.seat);
285
297
  } catch (_unused2) {
286
298
  throw new Error("Booking failed for seat ".concat(seatToBook.seat.r, " ").concat(seatToBook.seat.c));
287
299
  }
288
300
  };
289
301
  try {
290
- await Promise.all(seatsToBook.map(seatToBook => _tryBook(seatToBook)));
291
- setBookedSeats(prev => [...prev, ...seatsToBook.map(_ref2 => {
302
+ for (const seatToBook of seatsToBook) {
303
+ await _tryBook(seatToBook);
304
+ }
305
+ setRetrying(prev => prev.filter(id => !seatsToBook.some(s => s.seat.ssId === id)));
306
+ setBookedSeats(prev => [...prev, ...seatsToBook.map(_ref3 => {
292
307
  let {
293
308
  seat,
294
309
  priceage
295
- } = _ref2;
310
+ } = _ref3;
296
311
  return {
297
312
  ssId: seat.ssId,
298
313
  r: seat.r,
@@ -303,21 +318,30 @@ const SeatingPlan = _ref => {
303
318
  paName: priceage.paName
304
319
  };
305
320
  })]);
306
- updatedSeats.seats.forEach(s => {
307
- if (seatIds.includes(s.ssId)) {
308
- s.s = _utils.statuses.USER_PENDING;
309
- s.bookedPrice = price;
310
- s.loading = false;
311
- }
321
+ setSeats(prev => {
322
+ const updatedSeats = _objectSpread({}, prev);
323
+ seatsToBook.forEach(_ref4 => {
324
+ let {
325
+ seat
326
+ } = _ref4;
327
+ const seatIndex = updatedSeats.seats.findIndex(s => s.ssId === seat.ssId);
328
+ if (seatIndex !== -1) {
329
+ updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, updatedSeats.seats[seatIndex]), {}, {
330
+ loading: false,
331
+ s: _utils.statuses.USER_PENDING,
332
+ bookedPrice: price
333
+ });
334
+ }
335
+ });
336
+ return updatedSeats;
312
337
  });
313
- setSeats(updatedSeats);
314
338
  const event = {
315
339
  type: "cart-change-add",
316
- details: [...bookedSeats, ...seatsToBook.map(_ref3 => {
340
+ details: [...bookedSeats, ...seatsToBook.map(_ref5 => {
317
341
  let {
318
342
  seat,
319
343
  priceage
320
- } = _ref3;
344
+ } = _ref5;
321
345
  return {
322
346
  ssId: seat.ssId,
323
347
  r: seat.r,
@@ -331,6 +355,7 @@ const SeatingPlan = _ref => {
331
355
  };
332
356
  callbackFunction === null || callbackFunction === void 0 || callbackFunction(event);
333
357
  } catch (error) {
358
+ setRetrying(prev => prev.filter(id => !seatsToBook.some(s => s.seat.ssId === id)));
334
359
  console.error("Failed to book seats:", error);
335
360
  for (const seat of succeeded) {
336
361
  await fetch("".concat(apiUrl, "/seat/").concat(seat.ssId), {
@@ -343,24 +368,26 @@ const SeatingPlan = _ref => {
343
368
  setSeatsLoading(false);
344
369
  handleError({
345
370
  code: error === "429" ? 429 : 500,
346
- message: error === "429" ? _utils.ERROR_MESSAGES.SEAT_TAKEN : _utils.ERROR_MESSAGES.GENERIC_ERROR
371
+ message: _utils.ERROR_MESSAGES.GENERIC_ERROR
347
372
  });
348
- } finally {
349
- _initialFetch(true);
350
373
  }
351
374
  };
352
375
  const batchRemoveTicketsFromCart = async seatsToRemove => {
353
376
  if (!canMultiSelect && !seats.preventOrphanedSeats || seatsToRemove.length === 0) {
354
377
  return;
355
378
  }
356
- const updatedSeats = _objectSpread({}, seats);
357
- const seatIds = seatsToRemove.map(s => s.ssId);
358
- updatedSeats.seats.forEach(s => {
359
- if (seatIds.includes(s.ssId)) {
360
- s.loading = true;
361
- }
379
+ setSeats(prev => {
380
+ const updatedSeats = _objectSpread({}, prev);
381
+ seatsToRemove.forEach(s => {
382
+ const seatIndex = updatedSeats.seats.findIndex(seat => seat.ssId === s.ssId);
383
+ if (seatIndex !== -1) {
384
+ updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, updatedSeats.seats[seatIndex]), {}, {
385
+ loading: true
386
+ });
387
+ }
388
+ });
389
+ return updatedSeats;
362
390
  });
363
- setSeats(updatedSeats);
364
391
  const succeededIds = [];
365
392
  try {
366
393
  for (const seat of seatsToRemove) {
@@ -386,16 +413,20 @@ const SeatingPlan = _ref => {
386
413
  });
387
414
  }
388
415
  setBookedSeats(prev => [...prev.filter(v => !succeededIds.includes(v.ssId))]);
389
- updatedSeats.seats.forEach(s => {
390
- if (seatIds.includes(s.ssId)) {
391
- s.loading = false;
392
- if (succeededIds.includes(s.ssId)) {
393
- s.s = _utils.statuses.UNSOLD;
394
- s.bookedPrice = null;
416
+ setSeats(prev => {
417
+ const updatedSeats = _objectSpread({}, prev);
418
+ seatsToRemove.forEach(s => {
419
+ const seatIndex = updatedSeats.seats.findIndex(seat => seat.ssId === s.ssId);
420
+ if (seatIndex !== -1) {
421
+ updatedSeats.seats[seatIndex] = _objectSpread(_objectSpread({}, updatedSeats.seats[seatIndex]), {}, {
422
+ loading: false,
423
+ s: succeededIds.includes(s.ssId) ? _utils.statuses.UNSOLD : s.s,
424
+ bookedPrice: null
425
+ });
395
426
  }
396
- }
427
+ });
428
+ return updatedSeats;
397
429
  });
398
- setSeats(updatedSeats);
399
430
  setSeatsToRemove([]);
400
431
  if (succeededIds.length > 0) {
401
432
  const event = {
@@ -404,7 +435,6 @@ const SeatingPlan = _ref => {
404
435
  };
405
436
  callbackFunction === null || callbackFunction === void 0 || callbackFunction(event);
406
437
  }
407
- _initialFetch(true);
408
438
  };
409
439
  const handleClickSeat = (e, s) => {
410
440
  setChosenSeat({
@@ -538,7 +568,9 @@ const SeatingPlan = _ref => {
538
568
  pricing: seats.pricing,
539
569
  priceSectionIds: priceSectionIds,
540
570
  batchAddTicketsToCart: batchAddTicketsToCart
541
- }) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null), seats.preventOrphanedSeats && selectQuantityPopupOpen ? /*#__PURE__*/_react.default.createElement(_SelectQuantityPopup.default, {
571
+ }) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null), retrying.length > 0 ? /*#__PURE__*/_react.default.createElement("div", {
572
+ className: "retrying"
573
+ }, "Looks like things are busy right now. Please wait while we continue to confirm your selection...") : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null), seats.preventOrphanedSeats && selectQuantityPopupOpen ? /*#__PURE__*/_react.default.createElement(_SelectQuantityPopup.default, {
542
574
  quantity: desiredSeatQuantity,
543
575
  setQuantity: v => {
544
576
  setDesiredSeatQuantity(v);
@@ -1597,6 +1597,22 @@ svg.leaflet-image-layer.leaflet-interactive path {
1597
1597
  transform: translateX(-50%);
1598
1598
  }
1599
1599
 
1600
+ .retrying {
1601
+ position: absolute;
1602
+ top: 10px;
1603
+ left: 50%;
1604
+ transform: translateX(-50%);
1605
+ background: white;
1606
+ border-radius: 5px;
1607
+ padding: 0.5rem;
1608
+ font-size: 0.875rem;
1609
+ color: #333;
1610
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
1611
+ z-index: 1005;
1612
+ animation: fade-in 400ms;
1613
+ max-width: 50%;
1614
+ }
1615
+
1600
1616
  /* Printing */
1601
1617
 
1602
1618
  @media print {
@@ -42,5 +42,6 @@ const ERROR_MESSAGES = exports.ERROR_MESSAGES = {
42
42
  SEAT_TAKEN: "Someone else selected this first. Please try again",
43
43
  GENERIC_ERROR: "Oops! Something went wrong. Please try again.",
44
44
  MAX_QUANTITY: "Maximum ticket booking quantity specified reached.",
45
- NO_ALLOCATION: "Allocation exhausted, please try another area or price."
45
+ NO_ALLOCATION: "Allocation exhausted, please try another area or price.",
46
+ MAX_RETRIES: "Looks like things are busy and we couldn't complete your request. Please try again."
46
47
  };
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.8.2",
5
+ "version": "1.8.3",
6
6
  "private": false,
7
7
  "keywords": [
8
8
  "iticket",