@ticketboothapp/booking 1.2.63 → 1.2.65

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.
@@ -7,7 +7,6 @@ import {
7
7
  getAvailabilities,
8
8
  cancelReservation,
9
9
  cancelReservationBestEffort,
10
- createPaymentIntent,
11
10
  quoteChangeBooking,
12
11
  confirmFreeChangeBooking,
13
12
  createChangeBookingPaymentIntent,
@@ -62,10 +61,11 @@ import {
62
61
  } from '../../lib/booking/change-flow-pricing';
63
62
  import {
64
63
  mergePriceSummaryLinesForDrift,
64
+ mergeLineComparisonsWithFullDrift,
65
65
  normalizePricingDriftDetailFromQuote,
66
66
  normalizeTicketPricingTraceFromQuote,
67
67
  sumPriceSummaryLinesMajorUnits,
68
- enrichLineComparisonsWithMergedRows,
68
+ computePricingDriftDelta,
69
69
  } from '../../lib/booking/change-booking-pricing-drift';
70
70
  import { ChangeBookingPricingDriftPanel } from './ChangeBookingPricingDriftPanel';
71
71
  import {
@@ -109,7 +109,6 @@ export type { ChangeBookingFlowProps } from './booking-flow-types';
109
109
  * `serverPreview.priceSummaryLines` when the quote includes rows; otherwise falls back to FE-built lines after totals confirm.
110
110
  *
111
111
  * Until the first successful quote, `selfServeCheckoutPlaceholder` still avoids showing unchecked totals.
112
- *
113
112
  */
114
113
  function mergeQuoteSliceWithServerPreview(
115
114
  slice: ReturnType<typeof sliceChangeQuoteForUi>,
@@ -631,7 +630,7 @@ function resolveInitialAvailabilityFromBooking(
631
630
  }
632
631
 
633
632
  /**
634
- * Customer self-serve **change booking** only (no standard new-booking paths).
633
+ * Customer **change booking** self-serve flow (`quoteChangeBooking`, Stripe).
635
634
  * Duplicated from {@link NewBookingFlow} intentionally so each flow can evolve independently.
636
635
  */
637
636
  export function ChangeBookingFlow({
@@ -679,6 +678,7 @@ export function ChangeBookingFlow({
679
678
  onShowManage,
680
679
  getSuccessUrl,
681
680
  suppressCalendarDateScroll,
681
+ mode: bookingAppMode,
682
682
  } = useBookingApp();
683
683
  const availabilitiesCache = useAvailabilitiesCache();
684
684
  const [availabilities, setAvailabilities] = useState<Availability[]>([]);
@@ -799,6 +799,9 @@ export function ChangeBookingFlow({
799
799
  }, [initialValues?.dateTime, companyTimezone]);
800
800
  /** Do not render catalog-/FE-derived dollar amounts in UI until `quoteChangeBooking` returns `serverDisplay`. */
801
801
  const suppressSelfServeCurrencyUi = true;
802
+ /** Self-serve change flow only (no provider/admin pricing paths). */
803
+ const isCustomerSelfServeChange = true;
804
+ const isAdmin = false;
802
805
 
803
806
  useEffect(() => {
804
807
  setPartnerAttributionConfirmed(false);
@@ -1790,27 +1793,27 @@ export function ChangeBookingFlow({
1790
1793
 
1791
1794
  const initialAddOnMinQtyByKey = useMemo(() => {
1792
1795
  const map = new Map<string, number>();
1793
- if (false) return map;
1796
+ if (!isCustomerSelfServeChange) return map;
1794
1797
  for (const sel of initialAddOnBaselineSelections) {
1795
1798
  const key = `${sel.addOnId.trim()}::${sel.variantId?.trim() || ''}`;
1796
1799
  map.set(key, (map.get(key) ?? 0) + Math.max(1, Number(sel.quantity) || 1));
1797
1800
  }
1798
1801
  return map;
1799
- }, [true, initialAddOnBaselineSelections]);
1802
+ }, [isCustomerSelfServeChange, initialAddOnBaselineSelections]);
1800
1803
 
1801
1804
  const initialAddOnMinTotalByAddOnId = useMemo(() => {
1802
1805
  const map = new Map<string, number>();
1803
- if (false) return map;
1806
+ if (!isCustomerSelfServeChange) return map;
1804
1807
  for (const sel of initialAddOnBaselineSelections) {
1805
1808
  const addOnId = sel.addOnId.trim();
1806
1809
  map.set(addOnId, (map.get(addOnId) ?? 0) + Math.max(1, Number(sel.quantity) || 1));
1807
1810
  }
1808
1811
  return map;
1809
- }, [true, initialAddOnBaselineSelections]);
1812
+ }, [isCustomerSelfServeChange, initialAddOnBaselineSelections]);
1810
1813
 
1811
1814
  const applyChangeFlowAddOnFloor = useCallback(
1812
1815
  (nextSelections: Array<{ addOnId: string; variantId?: string; quantity?: number }>) => {
1813
- if (false || initialAddOnMinQtyByKey.size === 0) return nextSelections;
1816
+ if (!isCustomerSelfServeChange || initialAddOnMinQtyByKey.size === 0) return nextSelections;
1814
1817
  const qtyByKey = new Map<string, number>();
1815
1818
  for (const sel of nextSelections) {
1816
1819
  const key = `${sel.addOnId.trim()}::${sel.variantId?.trim() || ''}`;
@@ -1837,7 +1840,7 @@ export function ChangeBookingFlow({
1837
1840
  return { addOnId, variantId, quantity: qty };
1838
1841
  });
1839
1842
  },
1840
- [true, initialAddOnMinQtyByKey, initialAddOnMinTotalByAddOnId]
1843
+ [isCustomerSelfServeChange, initialAddOnMinQtyByKey, initialAddOnMinTotalByAddOnId]
1841
1844
  );
1842
1845
 
1843
1846
  const updateAddOnSelections = useCallback(
@@ -2451,14 +2454,14 @@ export function ChangeBookingFlow({
2451
2454
  const options = selectedAvailability?.returnOptions ?? [];
2452
2455
  const serverReturnMap = latestChangeQuote?.serverPreview?.returnOptionPriceByReturnAvailabilityId;
2453
2456
  const floor = effectiveChangeFlowReturnUnitFloorPerPerson;
2454
- const applyReturnFloor = floor != null && true;
2457
+ const applyReturnFloor = floor != null && isCustomerSelfServeChange;
2455
2458
  return options.map((opt) => {
2456
2459
  const vacancyCredit = changeFlowSeatCreditForReturnAvailabilityId(opt.returnAvailabilityId);
2457
2460
  const adjustedVacancies = Math.max(0, opt.vacancies ?? 0) + vacancyCredit;
2458
2461
  const rawPerPerson = opt.priceAdjustmentByCurrency?.[currency] ?? 0;
2459
2462
  const flooredPerPerson = applyReturnFloor ? Math.max(rawPerPerson, floor) : rawPerPerson;
2460
2463
  let perPerson = flooredPerPerson;
2461
- if (true && serverReturnMap && opt.returnAvailabilityId) {
2464
+ if (isCustomerSelfServeChange && serverReturnMap && opt.returnAvailabilityId) {
2462
2465
  const sid = opt.returnAvailabilityId.trim();
2463
2466
  const sp = serverReturnMap[sid];
2464
2467
  if (sp != null && Number.isFinite(sp)) {
@@ -2481,7 +2484,7 @@ export function ChangeBookingFlow({
2481
2484
  }, [
2482
2485
  selectedAvailability?.returnOptions,
2483
2486
  effectiveChangeFlowReturnUnitFloorPerPerson,
2484
- true,
2487
+ isCustomerSelfServeChange,
2485
2488
  currency,
2486
2489
  changeFlowSeatCreditForReturnAvailabilityId,
2487
2490
  latestChangeQuote?.serverPreview?.returnOptionPriceByReturnAvailabilityId,
@@ -2490,11 +2493,11 @@ export function ChangeBookingFlow({
2490
2493
  const selectedReturnOptionWithFloor = useMemo(() => {
2491
2494
  if (!selectedReturnOption) return selectedReturnOption;
2492
2495
  const floor = effectiveChangeFlowReturnUnitFloorPerPerson;
2493
- const applyReturnFloor = floor != null && true;
2496
+ const applyReturnFloor = floor != null && isCustomerSelfServeChange;
2494
2497
  const rawPerPerson = selectedReturnOption.priceAdjustmentByCurrency?.[currency] ?? 0;
2495
2498
  let perPerson = rawPerPerson;
2496
2499
  const serverReturnMap = latestChangeQuote?.serverPreview?.returnOptionPriceByReturnAvailabilityId;
2497
- if (true && serverReturnMap && selectedReturnOption.returnAvailabilityId) {
2500
+ if (isCustomerSelfServeChange && serverReturnMap && selectedReturnOption.returnAvailabilityId) {
2498
2501
  const sid = selectedReturnOption.returnAvailabilityId.trim();
2499
2502
  const sp = serverReturnMap[sid];
2500
2503
  if (sp != null && Number.isFinite(sp)) {
@@ -2515,7 +2518,7 @@ export function ChangeBookingFlow({
2515
2518
  }, [
2516
2519
  selectedReturnOption,
2517
2520
  effectiveChangeFlowReturnUnitFloorPerPerson,
2518
- true,
2521
+ isCustomerSelfServeChange,
2519
2522
  currency,
2520
2523
  latestChangeQuote?.serverPreview?.returnOptionPriceByReturnAvailabilityId,
2521
2524
  ]);
@@ -2556,7 +2559,7 @@ export function ChangeBookingFlow({
2556
2559
  /** Quote `ticketUnitPriceByCategory` overrides catalog units for ticket rows (customer self-serve only). */
2557
2560
  const pricingForTicketSelector = useMemo(() => {
2558
2561
  const m = latestChangeQuote?.serverPreview?.ticketUnitPriceByCategory;
2559
- if (false || !m) return pricing;
2562
+ if (!isCustomerSelfServeChange || !m) return pricing;
2560
2563
  return pricing.map((r) => {
2561
2564
  const u = m[r.category.toUpperCase()];
2562
2565
  if (u == null || !Number.isFinite(u)) return r;
@@ -2567,7 +2570,7 @@ export function ChangeBookingFlow({
2567
2570
  baseInDisplayCurrency: u,
2568
2571
  };
2569
2572
  });
2570
- }, [pricing, latestChangeQuote?.serverPreview?.ticketUnitPriceByCategory, true]);
2573
+ }, [pricing, latestChangeQuote?.serverPreview?.ticketUnitPriceByCategory, isCustomerSelfServeChange]);
2571
2574
 
2572
2575
  // Price breakdown: mid-layer returns line items (base + one per rule/deal). UI renders each line; rate in brackets when used.
2573
2576
  const getPriceBreakdown = useCallback((category: string, priceCAD: number, baseInDisplayCurrency: number | undefined, appliedAdjustments: Array<{ type: string; id: string; name: string; changeByCurrency?: Record<string, number> }> = []): PriceBreakdownData | null => {
@@ -2672,7 +2675,7 @@ export function ChangeBookingFlow({
2672
2675
 
2673
2676
  // When return selection (or refreshed availabilities) caps the party below current ticket counts, trim tickets (non-admin).
2674
2677
  useEffect(() => {
2675
- if (false || !selectedAvailability) return;
2678
+ if (isAdmin || !selectedAvailability) return;
2676
2679
  const cap = effectivePartySizeCap;
2677
2680
  if (totalQuantity <= cap) return;
2678
2681
  const over = totalQuantity - cap;
@@ -2700,7 +2703,7 @@ export function ChangeBookingFlow({
2700
2703
  effectivePartySizeCap,
2701
2704
  totalQuantity,
2702
2705
  selectedAvailability,
2703
- false,
2706
+ isAdmin,
2704
2707
  changeBookingMinimumQuantities,
2705
2708
  ]);
2706
2709
 
@@ -2774,7 +2777,6 @@ export function ChangeBookingFlow({
2774
2777
  */
2775
2778
  const changeFlowProtectedReturnAdjustment = useMemo(() => {
2776
2779
  if (totalQuantity <= 0) return returnPriceAdjustment;
2777
- if (false) return returnPriceAdjustment;
2778
2780
  if (effectiveChangeFlowReturnUnitFloorPerPerson == null) return returnPriceAdjustment;
2779
2781
  const livePerPerson =
2780
2782
  returnOptionCatalogPerPerson ?? (selectedReturnOption?.priceAdjustmentByCurrency?.[currency] ?? 0);
@@ -2788,7 +2790,6 @@ export function ChangeBookingFlow({
2788
2790
  }, [
2789
2791
  totalQuantity,
2790
2792
  returnPriceAdjustment,
2791
- false,
2792
2793
  effectiveChangeFlowReturnUnitFloorPerPerson,
2793
2794
  selectedReturnOption,
2794
2795
  returnOptionCatalogPerPerson,
@@ -2799,7 +2800,7 @@ export function ChangeBookingFlow({
2799
2800
 
2800
2801
  /** Return row amount for PriceSummary, Stripe breakdown, and CheckoutModal (catalog vs protected same-product-option). */
2801
2802
  const checkoutReturnLineAmount = useMemo(() => {
2802
- if (true) {
2803
+ if (isCustomerSelfServeChange) {
2803
2804
  return changeFlowProtectedReturnAdjustment;
2804
2805
  }
2805
2806
  if (changeFlowApplyReceiptPaidFloors) {
@@ -2807,7 +2808,7 @@ export function ChangeBookingFlow({
2807
2808
  }
2808
2809
  return returnPriceAdjustment;
2809
2810
  }, [
2810
- true,
2811
+ isCustomerSelfServeChange,
2811
2812
  changeFlowApplyReceiptPaidFloors,
2812
2813
  changeFlowProtectedReturnAdjustment,
2813
2814
  returnPriceAdjustment,
@@ -2879,13 +2880,12 @@ export function ChangeBookingFlow({
2879
2880
  return [...feeLineItems, ...addOnLines];
2880
2881
  }, [feeLineItems, addOnSelections, addOns]);
2881
2882
 
2883
+
2882
2884
  const checkoutPriceSummaryLines = useMemo((): PriceSummaryLine[] => {
2883
2885
  if (!selectedAvailability) return [];
2884
2886
  const returnLineAmount = checkoutReturnLineAmount;
2885
- /** Show row when a return is selected and either the priced amount is non-zero or a receipt return floor exists (catalog slot can be $0 while floored total is not). */
2886
2887
  const showReturnLine =
2887
- Boolean(selectedReturnOption) &&
2888
- (Math.abs(returnLineAmount) > 0.0005 || effectiveChangeFlowReturnUnitFloorPerPerson != null);
2888
+ Boolean(selectedReturnOption) && Math.abs(returnLineAmount) > 0.0005;
2889
2889
  return [
2890
2890
  ...ticketLineItemsForChangeFlowDisplay.map((line): PriceSummaryLine => {
2891
2891
  const rate = pricing.find((r) => r.category === line.category);
@@ -2897,8 +2897,6 @@ export function ChangeBookingFlow({
2897
2897
  );
2898
2898
  return {
2899
2899
  kind: 'ticket',
2900
- lineKey: undefined,
2901
- editable: false,
2902
2900
  category: line.category,
2903
2901
  qty: line.qty,
2904
2902
  itemTotal: line.itemTotal,
@@ -2933,8 +2931,6 @@ export function ChangeBookingFlow({
2933
2931
  fee.name.toLowerCase().includes('license'));
2934
2932
  return {
2935
2933
  kind: 'line' as const,
2936
- lineKey: undefined,
2937
- editable: false,
2938
2934
  label: feeLineItems.some((f) => f.name === fee.name)
2939
2935
  ? `${fee.name} (${totalQuantity} ${totalQuantity === 1 ? 'person' : 'people'})`
2940
2936
  : fee.name,
@@ -2960,7 +2956,6 @@ export function ChangeBookingFlow({
2960
2956
  effectiveCancellationPolicyLabel,
2961
2957
  feeLineItemsWithAddOns,
2962
2958
  feeLineItems,
2963
- effectiveChangeFlowReturnUnitFloorPerPerson,
2964
2959
  ]);
2965
2960
 
2966
2961
  const checkoutPriceSummaryLinesForCheckout = useMemo(() => {
@@ -3131,7 +3126,45 @@ export function ChangeBookingFlow({
3131
3126
 
3132
3127
  const clientMappedFromApi = mapQuoteLineItemsToPriceSummaryLines(api?.clientLineItems);
3133
3128
  const useBeClientLines = clientMappedFromApi.length > 0;
3134
- const clientLinesForMerge = useBeClientLines ? clientMappedFromApi : checkoutPriceSummaryLines;
3129
+ /**
3130
+ * Checkout passes tax/discount via PriceSummary props, not always as `priceSummaryLines`. Include them here so
3131
+ * drift rows and their sums align with `changeFlowNewBookingTotal` (subtotal + tax − promo).
3132
+ */
3133
+ const clientLinesForMerge: PriceSummaryLine[] = (() => {
3134
+ if (useBeClientLines) return clientMappedFromApi;
3135
+ const lines: PriceSummaryLine[] = [...checkoutPriceSummaryLines];
3136
+ const hasTaxLine = lines.some(
3137
+ (l) => l.kind === 'line' && String(l.type ?? '').toUpperCase() === 'TAX',
3138
+ );
3139
+ if (!hasTaxLine && !isTaxIncludedInPrice && Math.abs(effectiveTax) >= 0.005) {
3140
+ lines.push({
3141
+ kind: 'line',
3142
+ label: t('booking.tax') !== 'booking.tax' ? t('booking.tax') : 'Taxes and fees',
3143
+ amount: effectiveTax,
3144
+ type: 'TAX',
3145
+ });
3146
+ }
3147
+ const hasPromoSummaryLine = lines.some(
3148
+ (l) =>
3149
+ l.kind === 'line' &&
3150
+ (/PROMO|DISCOUNT|VOUCHER|GIFT/i.test(String(l.type ?? '')) ||
3151
+ (l.amount < -0.005 && /promo|discount/i.test(l.label))),
3152
+ );
3153
+ if (!hasPromoSummaryLine && Math.abs(effectivePromoDiscountAmount) >= 0.005) {
3154
+ const trimmedPromoCode = appliedPromoCode?.trim() ?? '';
3155
+ const promoLabel =
3156
+ trimmedPromoCode.length > 0
3157
+ ? `Promo: ${trimmedPromoCode}`
3158
+ : originalReceipt?.promoLabel?.trim() || 'Discount';
3159
+ lines.push({
3160
+ kind: 'line',
3161
+ label: promoLabel,
3162
+ amount: -effectivePromoDiscountAmount,
3163
+ type: 'PROMO_CODE',
3164
+ });
3165
+ }
3166
+ return lines;
3167
+ })();
3135
3168
 
3136
3169
  const serverMappedFromApi = mapQuoteLineItemsToPriceSummaryLines(api?.serverLineItems);
3137
3170
  const useBeServerLines = serverMappedFromApi.length > 0;
@@ -3175,14 +3208,35 @@ export function ChangeBookingFlow({
3175
3208
  }
3176
3209
 
3177
3210
  const mergedForDrift = mergePriceSummaryLinesForDrift(clientLinesForMerge, serverLinesForMerge);
3178
- let rows =
3211
+ const rows =
3179
3212
  api?.lineComparisons && api.lineComparisons.length > 0
3180
- ? enrichLineComparisonsWithMergedRows(api.lineComparisons, mergedForDrift)
3213
+ ? mergeLineComparisonsWithFullDrift(api.lineComparisons, mergedForDrift)
3181
3214
  : mergedForDrift;
3182
3215
 
3216
+ const receiptAnchoring = api?.receiptAnchoring;
3217
+ let driftRows = rows;
3218
+ if (receiptAnchoring) {
3219
+ const adj = roundMoney(
3220
+ receiptAnchoring.reconciledTotalMajorUnits -
3221
+ receiptAnchoring.requestedCatalogLineSumMajorUnits,
3222
+ );
3223
+ if (Math.abs(adj) >= 0.005) {
3224
+ driftRows = [
3225
+ ...rows,
3226
+ {
3227
+ key: 'meta:receipt-anchoring',
3228
+ label: 'Receipt anchoring adjustment (BE only — not included in app total)',
3229
+ clientAmount: null,
3230
+ serverAmount: adj,
3231
+ delta: computePricingDriftDelta(null, adj),
3232
+ },
3233
+ ];
3234
+ }
3235
+ }
3236
+
3183
3237
  const hasTotalDelta = totalDelta != null && Math.abs(totalDelta) >= 0.005;
3184
3238
 
3185
- if (rows.length === 0 && !hasTotalDelta) {
3239
+ if (driftRows.length === 0 && !hasTotalDelta) {
3186
3240
  return null;
3187
3241
  }
3188
3242
 
@@ -3211,7 +3265,7 @@ export function ChangeBookingFlow({
3211
3265
 
3212
3266
  return (
3213
3267
  <ChangeBookingPricingDriftPanel
3214
- rows={rows}
3268
+ rows={driftRows}
3215
3269
  clientTotal={clientTotalForDrift}
3216
3270
  serverTotal={serverTotalFromQuote}
3217
3271
  totalDelta={totalDelta}
@@ -3219,6 +3273,7 @@ export function ChangeBookingFlow({
3219
3273
  locale={locale}
3220
3274
  ticketCartDetail={ticketCartDetail.length > 0 ? ticketCartDetail : undefined}
3221
3275
  serverTicketPricingTrace={latestChangeQuote.ticketPricingTrace ?? undefined}
3276
+ receiptAnchoring={receiptAnchoring}
3222
3277
  footnote={
3223
3278
  usesBeLinePayload
3224
3279
  ? 'Lines use pricingDriftDetail.clientLineItems / serverLineItems when the quote includes them; totals prefer explicit major-unit fields, then sums of those lines, then the live cart / receipt preview.'
@@ -3237,6 +3292,12 @@ export function ChangeBookingFlow({
3237
3292
  locale,
3238
3293
  ticketLineItemsForChangeFlowDisplay,
3239
3294
  pricingForTicketSelector,
3295
+ isTaxIncludedInPrice,
3296
+ effectiveTax,
3297
+ effectivePromoDiscountAmount,
3298
+ appliedPromoCode,
3299
+ originalReceipt?.promoLabel,
3300
+ t,
3240
3301
  ]);
3241
3302
 
3242
3303
  /** Replaces PriceSummary with non-numeric status until quote returns authoritative totals (no FE dollar amounts). */
@@ -3398,8 +3459,7 @@ export function ChangeBookingFlow({
3398
3459
  countsChanged ||
3399
3460
  addOnsChanged ||
3400
3461
  returnChanged,
3401
- // Authoritative for "real user change" gating in provider dashboard:
3402
- // ignore option-id noise and only consider user-visible booking deltas.
3462
+ // Authoritative for "real user change" gating: ignore option-id noise; only user-visible deltas.
3403
3463
  hasOperationalChangesFromInitial:
3404
3464
  dateChanged ||
3405
3465
  pickupChanged ||
@@ -3429,28 +3489,25 @@ export function ChangeBookingFlow({
3429
3489
  const hasChangeSelection = changeSelectionDetails.hasChangesFromInitial;
3430
3490
 
3431
3491
  const changeFlowNeedsServerPrice =
3432
- true &&
3433
3492
  hasChangeSelection &&
3434
3493
  !!initialValues?.bookingReference?.trim() &&
3435
3494
  !!lastName.trim();
3436
3495
 
3437
- const isChangeQuoteBlocked = true && latestChangeQuote?.canProceed === false;
3438
- const requiresReturnInChangeFlow = true && !!initialValues?.returnAvailabilityId?.trim();
3496
+ const isChangeQuoteBlocked = latestChangeQuote?.canProceed === false;
3497
+ const requiresReturnInChangeFlow = !!initialValues?.returnAvailabilityId?.trim();
3439
3498
  const missingRequiredReturnSelection = requiresReturnInChangeFlow && !selectedReturnOption;
3440
3499
 
3441
3500
  const changeFlowSubmitDisabled =
3442
3501
  missingRequiredReturnSelection ||
3443
- (true &&
3444
- changeFlowNeedsServerPrice &&
3502
+ (changeFlowNeedsServerPrice &&
3445
3503
  (changeQuoteLoading || (!latestChangeQuote && !changeQuoteFetchError)));
3446
3504
 
3447
- const providerTotalsPreview = null;
3448
3505
  const hasEffectiveChangeSelection = hasChangeSelection;
3449
3506
 
3450
3507
  const displayedChangeAmounts = resolveChangeFlowDisplayedAmounts({
3451
- providerPreview: providerTotalsPreview,
3508
+ providerPreview: null,
3452
3509
  serverQuotePreview:
3453
- true && latestChangeQuote?.serverDisplay
3510
+ latestChangeQuote?.serverDisplay
3454
3511
  ? latestChangeQuote.serverDisplay
3455
3512
  : null,
3456
3513
  fromCart: {
@@ -3466,7 +3523,7 @@ export function ChangeBookingFlow({
3466
3523
  const changeFlowClientEstimateDue = (() => {
3467
3524
  if (!originalReceipt) return totalPrice;
3468
3525
  // Customer self-serve: amount due comes from POST .../change/quote (`amountDueCents` / priceDiff), not FE delta math.
3469
- if (true && latestChangeQuote != null && !changeQuoteFetchError) {
3526
+ if (latestChangeQuote != null && !changeQuoteFetchError) {
3470
3527
  return normalizeNearZeroOwed(latestChangeQuote.priceDiff);
3471
3528
  }
3472
3529
  return changeFlowBalanceVsOriginal({
@@ -3514,8 +3571,8 @@ export function ChangeBookingFlow({
3514
3571
  const checkoutFormError =
3515
3572
  (error || '') ||
3516
3573
  (missingRequiredReturnSelection ? 'Removing return option in self-serve is not available. Please contact support.' : '') ||
3517
- (true && isChangeQuoteBlocked ? (latestChangeQuote?.reasonIfBlocked ?? '') : '') ||
3518
- (true ? changeQuoteFetchError ?? '' : '');
3574
+ (isChangeQuoteBlocked ? (latestChangeQuote?.reasonIfBlocked ?? '') : '') ||
3575
+ (changeQuoteFetchError ?? '');
3519
3576
 
3520
3577
  const changeFlowSelectionPreview = useMemo((): ChangeFlowSelectionPreview | null => {
3521
3578
  if (!selectedAvailability || totalQuantity <= 0) return null;
@@ -3621,7 +3678,7 @@ export function ChangeBookingFlow({
3621
3678
 
3622
3679
  /** Debounced server quote so CTA + “amount owed” match PaymentIntent; avoids free confirm when FE estimate ≠ BE. */
3623
3680
  useEffect(() => {
3624
- if (false) {
3681
+ if (!isCustomerSelfServeChange) {
3625
3682
  setChangeQuoteLoading(false);
3626
3683
  setChangeQuoteFetchError(null);
3627
3684
  setLatestChangeQuote(null);
@@ -3712,7 +3769,7 @@ export function ChangeBookingFlow({
3712
3769
  window.clearTimeout(timer);
3713
3770
  };
3714
3771
  }, [
3715
- true,
3772
+ isCustomerSelfServeChange,
3716
3773
  hasChangeSelection,
3717
3774
  selectedAvailability,
3718
3775
  selectedAvailability?.dateTime,
@@ -3900,7 +3957,7 @@ export function ChangeBookingFlow({
3900
3957
  selectedAvailability,
3901
3958
  selectedReturnOption,
3902
3959
  companyTimezone,
3903
- false,
3960
+ isAdmin,
3904
3961
  initialValues?.returnAvailabilityId,
3905
3962
  initialValues?.returnDateTime,
3906
3963
  ]);
@@ -3963,9 +4020,9 @@ export function ChangeBookingFlow({
3963
4020
  const firstWithInventory = dates.find((d) => {
3964
4021
  const rows = availabilitiesByDate[d] ?? [];
3965
4022
  if (rows.length === 0) return false;
3966
- if (false) return rows.some((a) => (a.vacancies ?? 0) > 0);
4023
+ if (isAdmin) return rows.some((a) => (a.vacancies ?? 0) > 0);
3967
4024
  if (
3968
- true &&
4025
+ isCustomerSelfServeChange &&
3969
4026
  changeFlowInitialTicketCount > 0
3970
4027
  ) {
3971
4028
  return rows.some(
@@ -3974,7 +4031,7 @@ export function ChangeBookingFlow({
3974
4031
  }
3975
4032
  return rows.some((a) => (a.vacancies ?? 0) > 0);
3976
4033
  });
3977
- const first = firstWithInventory ?? (false && dates[0] ? dates[0] : undefined);
4034
+ const first = firstWithInventory ?? (isAdmin && dates[0] ? dates[0] : undefined);
3978
4035
  if (!first) return;
3979
4036
 
3980
4037
  hasAutoSelectedPartnerDateRef.current = true;
@@ -3997,8 +4054,8 @@ export function ChangeBookingFlow({
3997
4054
  selectedDate,
3998
4055
  dates,
3999
4056
  availabilitiesByDate,
4000
- false,
4001
- true,
4057
+ isAdmin,
4058
+ isCustomerSelfServeChange,
4002
4059
  changeFlowInitialTicketCount,
4003
4060
  getCalendarEffectiveOutboundVacancies,
4004
4061
  useWindowScroll,
@@ -4036,7 +4093,7 @@ export function ChangeBookingFlow({
4036
4093
  };
4037
4094
 
4038
4095
  const handleQuantityChange = (category: string, delta: number) => {
4039
- const maxAvailable = false ? Number.MAX_SAFE_INTEGER : effectivePartySizeCap;
4096
+ const maxAvailable = isAdmin ? Number.MAX_SAFE_INTEGER : effectivePartySizeCap;
4040
4097
  const currentQty = quantities[category] || 0;
4041
4098
  const minQ =
4042
4099
  changeBookingMinimumQuantities != null
@@ -4044,7 +4101,7 @@ export function ChangeBookingFlow({
4044
4101
  : 0;
4045
4102
  const newQty = Math.max(minQ, currentQty + delta);
4046
4103
  // Admin can overbook; non-admin cannot exceed vacancies
4047
- if (delta > 0 && !false && orderSummary.totalQuantity >= maxAvailable) {
4104
+ if (delta > 0 && !isAdmin && orderSummary.totalQuantity >= maxAvailable) {
4048
4105
  return;
4049
4106
  }
4050
4107
  setQuantities(prev => ({
@@ -4177,28 +4234,28 @@ export function ChangeBookingFlow({
4177
4234
  return;
4178
4235
  }
4179
4236
 
4180
- // Validate email (required)
4181
- if (!email) {
4182
- setError(t('booking.enterEmail') || 'Please enter your email address');
4183
- return;
4184
- }
4237
+ // Validate email (required)
4238
+ if (!email) {
4239
+ setError(t('booking.enterEmail') || 'Please enter your email address');
4240
+ return;
4241
+ }
4185
4242
 
4186
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
4187
- setError(t('booking.invalidEmail') || 'Please enter a valid email address');
4188
- return;
4189
- }
4243
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
4244
+ setError(t('booking.invalidEmail') || 'Please enter a valid email address');
4245
+ return;
4246
+ }
4190
4247
 
4191
- // Validate first name (required)
4192
- if (!firstName?.trim()) {
4193
- setError(t('booking.enterFirstName') || 'Please enter your first name');
4194
- return;
4195
- }
4248
+ // Validate first name (required)
4249
+ if (!firstName?.trim()) {
4250
+ setError(t('booking.enterFirstName') || 'Please enter your first name');
4251
+ return;
4252
+ }
4196
4253
 
4197
- // Validate last name (required for manage booking lookup)
4198
- if (!lastName?.trim()) {
4199
- setError(t('booking.enterLastName') || 'Please enter your last name');
4200
- return;
4201
- }
4254
+ // Validate last name (required for manage booking lookup)
4255
+ if (!lastName?.trim()) {
4256
+ setError(t('booking.enterLastName') || 'Please enter your last name');
4257
+ return;
4258
+ }
4202
4259
 
4203
4260
  // Allow checkout if pickup location is selected OR if user chose "I don't know"
4204
4261
  if (product.pickupLocations && product.pickupLocations.length > 0 && !pickupLocationId && !pickupLocationSkipped) {
@@ -4225,24 +4282,23 @@ export function ChangeBookingFlow({
4225
4282
  return;
4226
4283
  }
4227
4284
 
4228
-
4229
4285
  const bookingSourceContext = buildBookingSourceContext(bookingSourceAttribution, {
4230
4286
  clientChannelSource: inferClientBookingSourceFromProductIds(
4231
4287
  product.productId,
4232
4288
  availabilityProductOptionId,
4233
4289
  ),
4234
4290
  forcePartnerPortalChannel: partnerPortalBooking,
4235
- forceDashboardSource: false,
4291
+ forceDashboardSource: bookingAppMode === 'provider-dashboard',
4236
4292
  });
4237
4293
 
4238
4294
  // Get the hotel name if a pickup location was selected
4239
4295
  const selectedPickupLocation = pickupLocationId
4240
4296
  ? product.pickupLocations?.find(loc => loc.id === pickupLocationId)
4241
4297
  : null;
4298
+ let quotedPriceDiff: number | null = null;
4242
4299
  let changeIntentIdForCheckout: string | undefined;
4243
4300
  let changeBookingReferenceForPaidFlow: string | undefined;
4244
4301
 
4245
- {
4246
4302
  const changeBookingReference = initialValues?.bookingReference?.trim();
4247
4303
  const changeLastName = lastName.trim();
4248
4304
  if (!changeBookingReference || !changeLastName) {
@@ -4270,6 +4326,7 @@ export function ChangeBookingFlow({
4270
4326
  },
4271
4327
  currency
4272
4328
  );
4329
+ quotedPriceDiff = quoteSlice.priceDiff;
4273
4330
  changeBookingReferenceForPaidFlow = changeBookingReference;
4274
4331
  changeIntentIdForCheckout = quoteSlice.changeIntentId ?? undefined;
4275
4332
  setLatestChangeQuote(
@@ -4329,13 +4386,12 @@ export function ChangeBookingFlow({
4329
4386
  if (!changeIntentIdForCheckout) {
4330
4387
  throw new Error('Missing change intent for payment.');
4331
4388
  }
4332
- }
4333
4389
 
4334
4390
  pendingReservationRef.current = null;
4335
4391
 
4336
4392
  // Note: Do NOT call onSuccess here for paid bookings — we're about to show the Stripe
4337
4393
  // CheckoutModal. onSuccess (e.g. closing the parent dialog) should only run when we're
4338
- // actually done (free booking redirect, admin confirm-without-payment). Calling it here
4394
+ // actually done (free booking redirect). Calling it here
4339
4395
  // would close the dialog before the payment modal opens.
4340
4396
 
4341
4397
  // Update stored booking data (no holds reservation — change flow keys off booking reference elsewhere).
@@ -4362,10 +4418,10 @@ export function ChangeBookingFlow({
4362
4418
  // Backend will charge totalAmount and store this as the receipt so /manage matches.
4363
4419
  const taxForBreakdown = effectivePromoDiscountAmount > 0 ? effectiveTax : tax;
4364
4420
  const amountDueForCheckout = changeFlowBalanceVsOriginal({
4365
- newTotal: changeFlowNewBookingTotal,
4366
- originalReceiptTotal: originalReceipt?.total ?? 0,
4367
- audience: 'customer',
4368
- });
4421
+ newTotal: changeFlowNewBookingTotal,
4422
+ originalReceiptTotal: originalReceipt?.total ?? 0,
4423
+ audience: 'customer',
4424
+ });
4369
4425
  const lines = [
4370
4426
  ...ticketLineItemsForChangeFlowDisplay.map((line) => ({
4371
4427
  label: line.category,
@@ -4452,7 +4508,6 @@ export function ChangeBookingFlow({
4452
4508
  })()
4453
4509
  );
4454
4510
 
4455
-
4456
4511
  const ticketLinesForModal: CheckoutModalLineItem[] = ticketLineItemsForChangeFlowDisplay.map((line) => {
4457
4512
  const rate = pricing.find((r) => r.category === line.category);
4458
4513
  const breakdown = getPriceBreakdown(
@@ -4473,7 +4528,7 @@ export function ChangeBookingFlow({
4473
4528
  // Paid change: always return to stable ref+lastName + explicit intent (not reservationRef).
4474
4529
  // /manage-booking runs bounded refresh only when `from=change_payment` (see manage-booking page).
4475
4530
  successUrlOverride:
4476
- true && changeBookingReferenceForPaidFlow
4531
+ changeBookingReferenceForPaidFlow
4477
4532
  ? (() => {
4478
4533
  const origin = typeof window !== 'undefined' ? window.location.origin : '';
4479
4534
  const ref = encodeURIComponent(
@@ -4501,7 +4556,7 @@ export function ChangeBookingFlow({
4501
4556
  promoDiscountAmount: effectivePromoDiscountAmount > 0 ? effectivePromoDiscountAmount : 0,
4502
4557
  discountLabel: appliedPromoCode ? `Promo: ${appliedPromoCode}` : (originalReceipt?.promoLabel || undefined),
4503
4558
  changeTotals:
4504
- true && originalReceipt
4559
+ originalReceipt
4505
4560
  ? {
4506
4561
  previousTotal: originalReceipt.total,
4507
4562
  newTotal: displayChangeFlowProposedTotal,
@@ -4570,8 +4625,6 @@ export function ChangeBookingFlow({
4570
4625
  }
4571
4626
  };
4572
4627
 
4573
-
4574
-
4575
4628
  if (activeOptions.length === 0) {
4576
4629
  return (
4577
4630
  <div className="flex items-center justify-center py-16">
@@ -4655,12 +4708,12 @@ export function ChangeBookingFlow({
4655
4708
  syncVisibleWeekToSelectedDate={true}
4656
4709
  selectableSoldOutDate={changeFlowOriginalDate}
4657
4710
  partySizeRequiredForCalendarSelection={
4658
- true && !false && changeFlowInitialTicketCount > 0
4711
+ isCustomerSelfServeChange && !isAdmin && changeFlowInitialTicketCount > 0
4659
4712
  ? changeFlowInitialTicketCount
4660
4713
  : undefined
4661
4714
  }
4662
4715
  getEffectiveVacancies={
4663
- true && !false && changeFlowInitialTicketCount > 0
4716
+ isCustomerSelfServeChange && !isAdmin && changeFlowInitialTicketCount > 0
4664
4717
  ? getCalendarEffectiveOutboundVacancies
4665
4718
  : undefined
4666
4719
  }
@@ -4684,7 +4737,7 @@ export function ChangeBookingFlow({
4684
4737
  earliestDate={earliestAvailabilityDate}
4685
4738
  onVisibleRangeChange={handleVisibleRangeChange}
4686
4739
  currency={currency}
4687
- showCapacity={false}
4740
+ showCapacity={isAdmin}
4688
4741
  extraDiscountPercent={calendarDiscountPercent}
4689
4742
  capDiscountBadgesToBookingDate={changeFlowOriginalDate}
4690
4743
  />
@@ -4740,7 +4793,7 @@ export function ChangeBookingFlow({
4740
4793
  selectedTicketCount={totalQuantity}
4741
4794
  optionsMap={optionsMap}
4742
4795
  hasAnyMostPopular={hasAnyMostPopular}
4743
- isAdmin={false}
4796
+ isAdmin={isAdmin}
4744
4797
  pickupLocationSkipped={pickupLocationSkipped}
4745
4798
  t={t}
4746
4799
  onTimeSelect={handleTimeSelect}
@@ -4756,7 +4809,7 @@ export function ChangeBookingFlow({
4756
4809
  companyTimezone={companyTimezone}
4757
4810
  currency={currency}
4758
4811
  locale={locale}
4759
- isAdmin={false}
4812
+ isAdmin={isAdmin}
4760
4813
  t={t}
4761
4814
  onReturnSelect={(option) => {
4762
4815
  const raw = selectedAvailability.returnOptions?.find(
@@ -4784,7 +4837,7 @@ export function ChangeBookingFlow({
4784
4837
  resourceCount={selectedReturnOption ? null : (selectedAvailability.resourceCount ?? null)}
4785
4838
  currency={currency}
4786
4839
  locale={locale}
4787
- isAdmin={false}
4840
+ isAdmin={isAdmin}
4788
4841
  isSimplifiedPricingView={isSimplifiedPricingView}
4789
4842
  t={t}
4790
4843
  onQuantityChange={handleQuantityChange}
@@ -4806,7 +4859,7 @@ export function ChangeBookingFlow({
4806
4859
  currency={currency}
4807
4860
  locale={locale}
4808
4861
  onSelectionsChange={updateAddOnSelections}
4809
- minimumTotalByAddOnId={initialAddOnMinTotalByAddOnId}
4862
+ minimumTotalByAddOnId={isCustomerSelfServeChange ? initialAddOnMinTotalByAddOnId : undefined}
4810
4863
  suppressPrices={false}
4811
4864
  />
4812
4865
  )}
@@ -4836,7 +4889,7 @@ export function ChangeBookingFlow({
4836
4889
  currency={currency}
4837
4890
  locale={locale}
4838
4891
  t={t}
4839
- extraBetweenTaxAndTotal={undefined}
4892
+ extraBetweenTaxAndTotal={<></>}
4840
4893
  extraBeforeSubtotal={undefined}
4841
4894
  firstName={firstName}
4842
4895
  lastName={lastName}
@@ -4844,7 +4897,7 @@ export function ChangeBookingFlow({
4844
4897
  onFirstNameChange={(v) => { setFirstName(v); setError(''); }}
4845
4898
  onLastNameChange={(v) => { setLastName(v); setError(''); }}
4846
4899
  onEmailChange={(v) => { setEmail(v); setError(''); }}
4847
- readOnlyContactFields={false}
4900
+ readOnlyContactFields
4848
4901
  pickupLocations={
4849
4902
  selectedDate && product.pickupLocations && product.pickupLocations.length > 0
4850
4903
  ? product.pickupLocations
@@ -4890,7 +4943,9 @@ export function ChangeBookingFlow({
4890
4943
  onCheckout={handleCheckout}
4891
4944
  submitLabel={changeCheckoutButtonLabel ?? deferredInvoiceSubmitLabel}
4892
4945
  hideSubmitButton={
4893
- showCheckoutModal || !hasEffectiveChangeSelection || isChangeQuoteBlocked
4946
+ showCheckoutModal ||
4947
+ !hasEffectiveChangeSelection ||
4948
+ isChangeQuoteBlocked
4894
4949
  }
4895
4950
  submitDisabled={changeFlowSubmitDisabled}
4896
4951
  attributionSummary={flowUi?.partnerAttributionSummary}