@ticketboothapp/booking 1.2.78 → 1.2.79

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ticketboothapp/booking",
3
- "version": "1.2.78",
3
+ "version": "1.2.79",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -936,6 +936,9 @@ export function AdminChangeBookingFlow({
936
936
  } | null>(null);
937
937
  const [changeQuoteLoading, setChangeQuoteLoading] = useState(false);
938
938
  const [changeQuoteFetchError, setChangeQuoteFetchError] = useState<string | null>(null);
939
+ /** Dedupe quote calls while user input payload is unchanged. */
940
+ const lastCompletedQuoteInputsKeyRef = useRef<string | null>(null);
941
+ const inFlightQuoteInputsKeyRef = useRef<string | null>(null);
939
942
  const selfServePricingConfirmed =
940
943
  suppressSelfServeCurrencyUi &&
941
944
  latestChangeQuote != null &&
@@ -3696,6 +3699,40 @@ export function AdminChangeBookingFlow({
3696
3699
  !!lastName.trim();
3697
3700
 
3698
3701
  const isChangeQuoteBlocked = isCustomerSelfServeChange && latestChangeQuote?.canProceed === false;
3702
+ const changeQuoteInputsKey = useMemo(() => JSON.stringify({
3703
+ bookingReference: initialValues?.bookingReference?.trim() ?? '',
3704
+ lastName: lastName.trim().toLowerCase(),
3705
+ optionId: selectedAvailability?.productOptionId?.trim() || activeOptions[0]?.optionId || '',
3706
+ dateTime: selectedAvailability?.dateTime ?? '',
3707
+ availabilityId: selectedAvailability?.availabilityId ?? null,
3708
+ pickupLocationId: pickupLocationId ?? null,
3709
+ returnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
3710
+ quantities,
3711
+ addOnSelections,
3712
+ adminCustomLinesAsAdditionalAdjustments,
3713
+ feReceiptSubtotal: adminFeAuthoritativeReceipt.subtotal,
3714
+ feReceiptTax: adminFeAuthoritativeReceipt.tax,
3715
+ feReceiptTotal: adminFeAuthoritativeReceipt.total,
3716
+ feReceiptLineItems: adminFeAuthoritativeReceipt.lineItems,
3717
+ useAdminFeAuthoritativeQuote,
3718
+ }), [
3719
+ initialValues?.bookingReference,
3720
+ lastName,
3721
+ selectedAvailability?.productOptionId,
3722
+ selectedAvailability?.dateTime,
3723
+ selectedAvailability?.availabilityId,
3724
+ activeOptions,
3725
+ pickupLocationId,
3726
+ selectedReturnOption?.returnAvailabilityId,
3727
+ quantities,
3728
+ addOnSelections,
3729
+ adminCustomLinesAsAdditionalAdjustments,
3730
+ adminFeAuthoritativeReceipt.subtotal,
3731
+ adminFeAuthoritativeReceipt.tax,
3732
+ adminFeAuthoritativeReceipt.total,
3733
+ adminFeAuthoritativeReceipt.lineItems,
3734
+ useAdminFeAuthoritativeQuote,
3735
+ ]);
3699
3736
  const requiresReturnInChangeFlow = isCustomerSelfServeChange && !!initialValues?.returnAvailabilityId?.trim();
3700
3737
  const missingRequiredReturnSelection = requiresReturnInChangeFlow && !selectedReturnOption;
3701
3738
 
@@ -4174,6 +4211,17 @@ export function AdminChangeBookingFlow({
4174
4211
  setLatestChangeQuote(null);
4175
4212
  setChangeQuoteLoading(false);
4176
4213
  setChangeQuoteFetchError(null);
4214
+ lastCompletedQuoteInputsKeyRef.current = null;
4215
+ inFlightQuoteInputsKeyRef.current = null;
4216
+ return;
4217
+ }
4218
+
4219
+ if (
4220
+ changeQuoteInputsKey &&
4221
+ (inFlightQuoteInputsKeyRef.current === changeQuoteInputsKey ||
4222
+ lastCompletedQuoteInputsKeyRef.current === changeQuoteInputsKey)
4223
+ ) {
4224
+ setChangeQuoteLoading(false);
4177
4225
  return;
4178
4226
  }
4179
4227
 
@@ -4192,6 +4240,7 @@ export function AdminChangeBookingFlow({
4192
4240
  const bookingReferenceForQuote = initialValues.bookingReference.trim();
4193
4241
  const timer = window.setTimeout(() => {
4194
4242
  void (async () => {
4243
+ inFlightQuoteInputsKeyRef.current = changeQuoteInputsKey;
4195
4244
  const bookingItems = Object.entries(quantities)
4196
4245
  .filter(([, count]) => count > 0)
4197
4246
  .map(([category, count]) => ({ category, count }));
@@ -4218,11 +4267,18 @@ export function AdminChangeBookingFlow({
4218
4267
  previousReturnAvailabilityId: initialValues.returnAvailabilityId ?? null,
4219
4268
  },
4220
4269
  };
4270
+ const deterministicClientProposedTotal = useAdminFeAuthoritativeQuote
4271
+ ? adminFeAuthoritativeReceipt.total
4272
+ : changeFlowNewBookingTotal;
4273
+ const deterministicAmountDue = originalReceipt
4274
+ ? roundMoney(adminFeAuthoritativeReceipt.total - originalReceipt.total)
4275
+ : roundMoney(changeFlowClientEstimateDue);
4221
4276
  const quote = useAdminFeAuthoritativeQuote
4222
4277
  ? await quoteChangeBookingAdminFeReceipt({
4223
4278
  ...quoteRequestBase,
4224
4279
  feReceipt: adminFeAuthoritativeReceipt,
4225
- feAmountDueMajorUnits: roundMoney(changeFlowClientEstimateDue),
4280
+ feAmountDueMajorUnits: deterministicAmountDue,
4281
+ clientProposedTotal: deterministicClientProposedTotal,
4226
4282
  })
4227
4283
  : await quoteChangeBooking(quoteRequestBase);
4228
4284
  if (seq !== changeQuoteRequestSeq.current) return;
@@ -4242,11 +4298,16 @@ export function AdminChangeBookingFlow({
4242
4298
  tax: effectiveTax,
4243
4299
  }, currency),
4244
4300
  );
4301
+ lastCompletedQuoteInputsKeyRef.current = changeQuoteInputsKey;
4245
4302
  } catch (e) {
4246
4303
  if (seq !== changeQuoteRequestSeq.current) return;
4247
4304
  setLatestChangeQuote(null);
4248
4305
  setChangeQuoteFetchError(e instanceof Error ? e.message : 'Failed to get price for this change');
4306
+ lastCompletedQuoteInputsKeyRef.current = null;
4249
4307
  } finally {
4308
+ if (inFlightQuoteInputsKeyRef.current === changeQuoteInputsKey) {
4309
+ inFlightQuoteInputsKeyRef.current = null;
4310
+ }
4250
4311
  if (seq === changeQuoteRequestSeq.current) {
4251
4312
  setChangeQuoteLoading(false);
4252
4313
  }
@@ -4283,6 +4344,8 @@ export function AdminChangeBookingFlow({
4283
4344
  adminCustomLinesAsAdditionalAdjustments,
4284
4345
  adminFeAuthoritativeReceipt,
4285
4346
  useAdminFeAuthoritativeQuote,
4347
+ changeQuoteInputsKey,
4348
+ originalReceipt?.total,
4286
4349
  ]);
4287
4350
 
4288
4351
  // Auto-select product option when date is selected: most popular if set, otherwise first available.
@@ -4817,13 +4880,19 @@ export function AdminChangeBookingFlow({
4817
4880
  ? { manualLineAdjustments: adminCustomLinesAsAdditionalAdjustments }
4818
4881
  : {}),
4819
4882
  clientProposedTotal:
4820
- latestChangeQuote?.serverDisplay?.total ?? changeFlowNewBookingTotal,
4883
+ useAdminFeAuthoritativeQuote
4884
+ ? adminFeAuthoritativeReceipt.total
4885
+ : (latestChangeQuote?.serverDisplay?.total ?? changeFlowNewBookingTotal),
4821
4886
  };
4822
4887
  const quote = useAdminFeAuthoritativeQuote
4823
4888
  ? await quoteChangeBookingAdminFeReceipt({
4824
4889
  ...quoteRequestBase,
4825
4890
  feReceipt: adminFeAuthoritativeReceipt,
4826
- feAmountDueMajorUnits: roundMoney(changeFlowClientEstimateDue),
4891
+ feAmountDueMajorUnits: roundMoney(
4892
+ originalReceipt
4893
+ ? adminFeAuthoritativeReceipt.total - originalReceipt.total
4894
+ : changeFlowClientEstimateDue
4895
+ ),
4827
4896
  })
4828
4897
  : await quoteChangeBooking(quoteRequestBase);
4829
4898
  const quoteSlice = sliceChangeQuoteForUi(