@ticketboothapp/booking 1.2.78 → 1.2.80

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.80",
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,38 @@ 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
+ adminCustomReceiptLines,
3713
+ editableCheckoutPriceSummaryLines,
3714
+ editableSummaryLineAmountInputs,
3715
+ editableSummaryLineLabelInputs,
3716
+ useAdminFeAuthoritativeQuote,
3717
+ }), [
3718
+ initialValues?.bookingReference,
3719
+ lastName,
3720
+ selectedAvailability?.productOptionId,
3721
+ selectedAvailability?.dateTime,
3722
+ selectedAvailability?.availabilityId,
3723
+ activeOptions,
3724
+ pickupLocationId,
3725
+ selectedReturnOption?.returnAvailabilityId,
3726
+ quantities,
3727
+ addOnSelections,
3728
+ adminCustomReceiptLines,
3729
+ editableCheckoutPriceSummaryLines,
3730
+ editableSummaryLineAmountInputs,
3731
+ editableSummaryLineLabelInputs,
3732
+ useAdminFeAuthoritativeQuote,
3733
+ ]);
3699
3734
  const requiresReturnInChangeFlow = isCustomerSelfServeChange && !!initialValues?.returnAvailabilityId?.trim();
3700
3735
  const missingRequiredReturnSelection = requiresReturnInChangeFlow && !selectedReturnOption;
3701
3736
 
@@ -4174,6 +4209,17 @@ export function AdminChangeBookingFlow({
4174
4209
  setLatestChangeQuote(null);
4175
4210
  setChangeQuoteLoading(false);
4176
4211
  setChangeQuoteFetchError(null);
4212
+ lastCompletedQuoteInputsKeyRef.current = null;
4213
+ inFlightQuoteInputsKeyRef.current = null;
4214
+ return;
4215
+ }
4216
+
4217
+ if (
4218
+ changeQuoteInputsKey &&
4219
+ (inFlightQuoteInputsKeyRef.current === changeQuoteInputsKey ||
4220
+ lastCompletedQuoteInputsKeyRef.current === changeQuoteInputsKey)
4221
+ ) {
4222
+ setChangeQuoteLoading(false);
4177
4223
  return;
4178
4224
  }
4179
4225
 
@@ -4192,6 +4238,7 @@ export function AdminChangeBookingFlow({
4192
4238
  const bookingReferenceForQuote = initialValues.bookingReference.trim();
4193
4239
  const timer = window.setTimeout(() => {
4194
4240
  void (async () => {
4241
+ inFlightQuoteInputsKeyRef.current = changeQuoteInputsKey;
4195
4242
  const bookingItems = Object.entries(quantities)
4196
4243
  .filter(([, count]) => count > 0)
4197
4244
  .map(([category, count]) => ({ category, count }));
@@ -4218,11 +4265,18 @@ export function AdminChangeBookingFlow({
4218
4265
  previousReturnAvailabilityId: initialValues.returnAvailabilityId ?? null,
4219
4266
  },
4220
4267
  };
4268
+ const deterministicClientProposedTotal = useAdminFeAuthoritativeQuote
4269
+ ? adminFeAuthoritativeReceipt.total
4270
+ : changeFlowNewBookingTotal;
4271
+ const deterministicAmountDue = originalReceipt
4272
+ ? roundMoney(adminFeAuthoritativeReceipt.total - originalReceipt.total)
4273
+ : roundMoney(changeFlowClientEstimateDue);
4221
4274
  const quote = useAdminFeAuthoritativeQuote
4222
4275
  ? await quoteChangeBookingAdminFeReceipt({
4223
4276
  ...quoteRequestBase,
4224
4277
  feReceipt: adminFeAuthoritativeReceipt,
4225
- feAmountDueMajorUnits: roundMoney(changeFlowClientEstimateDue),
4278
+ feAmountDueMajorUnits: deterministicAmountDue,
4279
+ clientProposedTotal: deterministicClientProposedTotal,
4226
4280
  })
4227
4281
  : await quoteChangeBooking(quoteRequestBase);
4228
4282
  if (seq !== changeQuoteRequestSeq.current) return;
@@ -4242,11 +4296,16 @@ export function AdminChangeBookingFlow({
4242
4296
  tax: effectiveTax,
4243
4297
  }, currency),
4244
4298
  );
4299
+ lastCompletedQuoteInputsKeyRef.current = changeQuoteInputsKey;
4245
4300
  } catch (e) {
4246
4301
  if (seq !== changeQuoteRequestSeq.current) return;
4247
4302
  setLatestChangeQuote(null);
4248
4303
  setChangeQuoteFetchError(e instanceof Error ? e.message : 'Failed to get price for this change');
4304
+ lastCompletedQuoteInputsKeyRef.current = null;
4249
4305
  } finally {
4306
+ if (inFlightQuoteInputsKeyRef.current === changeQuoteInputsKey) {
4307
+ inFlightQuoteInputsKeyRef.current = null;
4308
+ }
4250
4309
  if (seq === changeQuoteRequestSeq.current) {
4251
4310
  setChangeQuoteLoading(false);
4252
4311
  }
@@ -4283,6 +4342,8 @@ export function AdminChangeBookingFlow({
4283
4342
  adminCustomLinesAsAdditionalAdjustments,
4284
4343
  adminFeAuthoritativeReceipt,
4285
4344
  useAdminFeAuthoritativeQuote,
4345
+ changeQuoteInputsKey,
4346
+ originalReceipt?.total,
4286
4347
  ]);
4287
4348
 
4288
4349
  // Auto-select product option when date is selected: most popular if set, otherwise first available.
@@ -4817,13 +4878,19 @@ export function AdminChangeBookingFlow({
4817
4878
  ? { manualLineAdjustments: adminCustomLinesAsAdditionalAdjustments }
4818
4879
  : {}),
4819
4880
  clientProposedTotal:
4820
- latestChangeQuote?.serverDisplay?.total ?? changeFlowNewBookingTotal,
4881
+ useAdminFeAuthoritativeQuote
4882
+ ? adminFeAuthoritativeReceipt.total
4883
+ : (latestChangeQuote?.serverDisplay?.total ?? changeFlowNewBookingTotal),
4821
4884
  };
4822
4885
  const quote = useAdminFeAuthoritativeQuote
4823
4886
  ? await quoteChangeBookingAdminFeReceipt({
4824
4887
  ...quoteRequestBase,
4825
4888
  feReceipt: adminFeAuthoritativeReceipt,
4826
- feAmountDueMajorUnits: roundMoney(changeFlowClientEstimateDue),
4889
+ feAmountDueMajorUnits: roundMoney(
4890
+ originalReceipt
4891
+ ? adminFeAuthoritativeReceipt.total - originalReceipt.total
4892
+ : changeFlowClientEstimateDue
4893
+ ),
4827
4894
  })
4828
4895
  : await quoteChangeBooking(quoteRequestBase);
4829
4896
  const quoteSlice = sliceChangeQuoteForUi(