@ticketboothapp/booking 1.2.103 → 1.2.105

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.103",
3
+ "version": "1.2.105",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -333,18 +333,30 @@ function findMergedAvailabilityForSelection(
333
333
  selected: Availability | null
334
334
  ): Availability | undefined {
335
335
  if (!selected) return undefined;
336
- const optId = selected.productOptionId ?? undefined;
336
+ const optId = getAvailabilityOptionId(selected) || undefined;
337
337
  const dt = selected.dateTime;
338
338
  const availId = selected.availabilityId?.trim();
339
339
  if (availId && optId) {
340
340
  const exact = merged.find(
341
341
  (a) =>
342
342
  a.availabilityId === availId &&
343
- (a.productOptionId === optId || a.productOptionId === undefined)
343
+ getAvailabilityOptionId(a) === optId
344
344
  );
345
345
  if (exact) return exact;
346
346
  }
347
- return merged.find((a) => a.dateTime === dt && a.productOptionId === optId);
347
+ return merged.find((a) => a.dateTime === dt && getAvailabilityOptionId(a) === optId);
348
+ }
349
+
350
+ function getAvailabilityOptionId(availability: Availability, fallbackOptionId?: string): string {
351
+ const productOptionId = availability.productOptionId?.trim();
352
+ if (productOptionId) return productOptionId;
353
+ const productId = availability.productId?.trim();
354
+ if (productId?.startsWith('po_')) return productId;
355
+ return fallbackOptionId?.trim() ?? '';
356
+ }
357
+
358
+ function getAvailabilityCacheKey(availability: Availability): string {
359
+ return `${availability.dateTime}-${getAvailabilityOptionId(availability)}`;
348
360
  }
349
361
 
350
362
  /** Ticket rates for one availability row — mirrors main cart [pricing] useMemo so baseline slots match BE. */
@@ -358,7 +370,7 @@ function buildPricingFromAvailability(
358
370
  isSimplifiedPricingView: boolean,
359
371
  ) {
360
372
  if (!selectedAvailability || !pricingConfig) return [];
361
- const optionId = selectedAvailability.productOptionId;
373
+ const optionId = getAvailabilityOptionId(selectedAvailability);
362
374
  const selectedOption = activeOptions.find((opt) => opt.optionId === optionId);
363
375
  const precomputed = optionId ? precomputedPricesByOption?.[optionId] : undefined;
364
376
  const rateToDisplayPrice = (backendInDisplayCurrency: number) =>
@@ -376,11 +388,17 @@ function buildPricingFromAvailability(
376
388
  appliedAdjustments: Array<{ type: string; id: string; name: string; changeByCurrency?: Record<string, number> }>,
377
389
  ) => {
378
390
  const basePriceCAD = selectedOption?.pricing?.[category.toUpperCase()] ?? 0;
391
+ const hasOptionScopedPrice = baseInDisplayCurrency > 0;
392
+ const priceCADForBreakdown =
393
+ hasOptionScopedPrice && currency === 'CAD' ? baseInDisplayCurrency : backendPriceCAD;
394
+ const displayCurrencyPriceForFallback = hasOptionScopedPrice
395
+ ? baseInDisplayCurrency
396
+ : backendInDisplayCurrency;
379
397
  const isPublicMode = isSimplifiedPricingView;
380
398
  const breakdown = computePriceBreakdown(
381
399
  pricingConfig,
382
400
  currency,
383
- backendPriceCAD,
401
+ priceCADForBreakdown,
384
402
  basePriceCAD,
385
403
  hasFees,
386
404
  appliedAdjustments,
@@ -388,8 +406,8 @@ function buildPricingFromAvailability(
388
406
  baseInDisplayCurrency,
389
407
  isPublicMode,
390
408
  );
391
- const price = breakdown?.finalPrice ?? rateToDisplayPrice(backendInDisplayCurrency);
392
- return { category, baseInDisplayCurrency, appliedAdjustments, price, priceCAD: backendPriceCAD };
409
+ const price = breakdown?.finalPrice ?? rateToDisplayPrice(displayCurrencyPriceForFallback);
410
+ return { category, baseInDisplayCurrency, appliedAdjustments, price, priceCAD: priceCADForBreakdown };
393
411
  };
394
412
  return (
395
413
  selectedAvailability.rates?.map((rate) => {
@@ -974,6 +992,14 @@ export function AdminChangeBookingFlow({
974
992
  return '';
975
993
  }
976
994
  });
995
+ const selectedDateRangeExtensionKey = useMemo(() => {
996
+ if (!visibleRange || !selectedDate) return '';
997
+ try {
998
+ return isAfter(parseISO(selectedDate), visibleRange.end) ? selectedDate : '';
999
+ } catch {
1000
+ return '';
1001
+ }
1002
+ }, [selectedDate, visibleRange]);
977
1003
  const [isItinerarySticky, setIsItinerarySticky] = useState(false);
978
1004
  const isItineraryStickyRef = useRef(false);
979
1005
  const [isMobile, setIsMobile] = useState(false);
@@ -1273,10 +1299,10 @@ export function AdminChangeBookingFlow({
1273
1299
  let mergedOut: Availability[] = [];
1274
1300
  setAvailabilities((prev) => {
1275
1301
  const existingMap = new Map(
1276
- prev.map((avail) => [`${avail.dateTime}-${avail.productOptionId}`, avail])
1302
+ prev.map((avail) => [getAvailabilityCacheKey(avail), avail])
1277
1303
  );
1278
1304
  allFetchedAvailabilities.forEach((avail) => {
1279
- existingMap.set(`${avail.dateTime}-${avail.productOptionId}`, avail);
1305
+ existingMap.set(getAvailabilityCacheKey(avail), avail);
1280
1306
  });
1281
1307
  mergedOut = Array.from(existingMap.values());
1282
1308
  return mergedOut;
@@ -1303,16 +1329,17 @@ export function AdminChangeBookingFlow({
1303
1329
  activeOptionIdsKey,
1304
1330
  appliedPromoCode,
1305
1331
  pricingProfileIdForAvailabilities,
1332
+ cancellationPolicyProfileIdForAvailabilities,
1306
1333
  )
1307
1334
  : null;
1308
1335
  if (cacheKey && availabilitiesCache) {
1309
1336
  const existingCache = availabilitiesCache.get(cacheKey);
1310
1337
  const existingAvailabilities = existingCache?.availabilities ?? [];
1311
1338
  const mergedAvailabilitiesMap = new Map(
1312
- existingAvailabilities.map((a) => [`${a.dateTime}-${a.productOptionId}`, a])
1339
+ existingAvailabilities.map((a) => [getAvailabilityCacheKey(a), a])
1313
1340
  );
1314
1341
  allFetchedAvailabilities.forEach((a) => {
1315
- mergedAvailabilitiesMap.set(`${a.dateTime}-${a.productOptionId}`, a);
1342
+ mergedAvailabilitiesMap.set(getAvailabilityCacheKey(a), a);
1316
1343
  });
1317
1344
  const mergedPrecomputed = { ...(existingCache?.precomputedPricesByOption ?? {}) };
1318
1345
  results.forEach((r) => {
@@ -1427,11 +1454,11 @@ export function AdminChangeBookingFlow({
1427
1454
  ? endOfLatestDay
1428
1455
  : visibleRange.end;
1429
1456
 
1430
- // Ensure we include the selected date if it's after the visible range end
1431
- // This handles the case where user selects a date that's not yet in the visible range
1432
- if (selectedDate) {
1457
+ // Include an externally seeded selected date only when it falls outside the current calendar window.
1458
+ // Normal date clicks are already inside visibleRange and should not re-fetch the broad availability range.
1459
+ if (selectedDateRangeExtensionKey) {
1433
1460
  try {
1434
- const selectedDateObj = parseISO(selectedDate);
1461
+ const selectedDateObj = parseISO(selectedDateRangeExtensionKey);
1435
1462
  if (isAfter(selectedDateObj, clampedEnd)) {
1436
1463
  clampedEnd = selectedDateObj;
1437
1464
  }
@@ -1447,6 +1474,7 @@ export function AdminChangeBookingFlow({
1447
1474
  activeOptionIdsKey,
1448
1475
  appliedPromoCode,
1449
1476
  pricingProfileIdForAvailabilities,
1477
+ cancellationPolicyProfileIdForAvailabilities,
1450
1478
  )
1451
1479
  : null;
1452
1480
  const cached = cacheKey ? availabilitiesCache!.get(cacheKey) : undefined;
@@ -1588,12 +1616,12 @@ export function AdminChangeBookingFlow({
1588
1616
  // Merge with existing availabilities (avoid duplicates by dateTime + productOptionId)
1589
1617
  setAvailabilities(prev => {
1590
1618
  const existingMap = new Map(
1591
- prev.map(avail => [`${avail.dateTime}-${avail.productOptionId}`, avail])
1619
+ prev.map(avail => [getAvailabilityCacheKey(avail), avail])
1592
1620
  );
1593
1621
 
1594
1622
  // Merge new availabilities - update existing ones or add new ones
1595
1623
  allFetchedAvailabilities.forEach(avail => {
1596
- const key = `${avail.dateTime}-${avail.productOptionId}`;
1624
+ const key = getAvailabilityCacheKey(avail);
1597
1625
  // Always update to get latest data (vacancies, prices, etc.)
1598
1626
  existingMap.set(key, avail);
1599
1627
  });
@@ -1622,10 +1650,10 @@ export function AdminChangeBookingFlow({
1622
1650
  const existingCache = availabilitiesCache.get(cacheKey);
1623
1651
  const existingAvailabilities = existingCache?.availabilities ?? [];
1624
1652
  const mergedAvailabilitiesMap = new Map(
1625
- existingAvailabilities.map((a) => [`${a.dateTime}-${a.productOptionId}`, a])
1653
+ existingAvailabilities.map((a) => [getAvailabilityCacheKey(a), a])
1626
1654
  );
1627
1655
  allFetchedAvailabilities.forEach((a) => {
1628
- mergedAvailabilitiesMap.set(`${a.dateTime}-${a.productOptionId}`, a);
1656
+ mergedAvailabilitiesMap.set(getAvailabilityCacheKey(a), a);
1629
1657
  });
1630
1658
  const mergedPrecomputed = { ...(existingCache?.precomputedPricesByOption ?? {}) };
1631
1659
  results.forEach((r) => {
@@ -1673,7 +1701,7 @@ export function AdminChangeBookingFlow({
1673
1701
  activeOptionIdsKey,
1674
1702
  isPrivateShuttle,
1675
1703
  companyTimezone,
1676
- selectedDate,
1704
+ selectedDateRangeExtensionKey,
1677
1705
  appliedPromoCode,
1678
1706
  pricingProfileIdForAvailabilities,
1679
1707
  cancellationPolicyProfileIdForAvailabilities,
@@ -1871,7 +1899,7 @@ export function AdminChangeBookingFlow({
1871
1899
  timesForSelectedDate
1872
1900
  .map(
1873
1901
  (avail) =>
1874
- `${avail.dateTime}|${avail.productOptionId ?? ''}|${avail.vacancies ?? 0}`,
1902
+ `${avail.dateTime}|${getAvailabilityOptionId(avail)}|${avail.vacancies ?? 0}`,
1875
1903
  )
1876
1904
  .join('||'),
1877
1905
  [timesForSelectedDate],
@@ -2305,7 +2333,9 @@ export function AdminChangeBookingFlow({
2305
2333
  const calculateStaySummary = useCallback((returnDateTime: Date): string | null => {
2306
2334
  if (!selectedAvailability) return null;
2307
2335
 
2308
- const availabilityProductOptionId = selectedAvailability.productOptionId || activeOptions[0]?.optionId;
2336
+ const availabilityProductOptionId =
2337
+ getAvailabilityOptionId(selectedAvailability) ||
2338
+ (activeOptions.length === 1 ? activeOptions[0]?.optionId : undefined);
2309
2339
  const selectedOption = activeOptions.find(opt => opt.optionId === availabilityProductOptionId);
2310
2340
  if (!selectedOption) return null;
2311
2341
 
@@ -2381,7 +2411,9 @@ export function AdminChangeBookingFlow({
2381
2411
  // Helper function to compute itinerary display for storage (returns same shape as "Your Itinerary" box)
2382
2412
  const computeItineraryDisplay = useCallback((): ItineraryDisplayStep[] | null => {
2383
2413
  if (!selectedAvailability) return null;
2384
- const availabilityProductOptionId = selectedAvailability.productOptionId || activeOptions[0]?.optionId;
2414
+ const availabilityProductOptionId =
2415
+ getAvailabilityOptionId(selectedAvailability) ||
2416
+ (activeOptions.length === 1 ? activeOptions[0]?.optionId : undefined);
2385
2417
  const selectedOption = activeOptions.find(opt => opt.optionId === availabilityProductOptionId);
2386
2418
  if (!selectedOption) return null;
2387
2419
  const tourStartTime = parseISO(selectedAvailability.dateTime);
@@ -2620,13 +2652,13 @@ export function AdminChangeBookingFlow({
2620
2652
  const selectedMs = parseAvailabilityDateTime(selectedAvailability.dateTime).getTime();
2621
2653
  const initialMs = parseAvailabilityDateTime(initialValues.dateTime).getTime();
2622
2654
  if (selectedMs !== initialMs) return false;
2623
- const selectedOpt = normalizeProductOptionIdForChangeFlow(selectedAvailability.productOptionId);
2655
+ const selectedOpt = normalizeProductOptionIdForChangeFlow(getAvailabilityOptionId(selectedAvailability));
2624
2656
  const initialOpt = changeFlowResolvedInitialProductOptionId;
2625
2657
  if (selectedOpt != null && initialOpt != null) {
2626
2658
  return selectedOpt === initialOpt;
2627
2659
  }
2628
2660
  const legacyInitial = initialValues.productOptionId?.trim() || null;
2629
- const legacySelected = selectedAvailability.productOptionId?.trim() || null;
2661
+ const legacySelected = getAvailabilityOptionId(selectedAvailability) || null;
2630
2662
  return !legacySelected || !legacyInitial || legacySelected === legacyInitial;
2631
2663
  }, [
2632
2664
  selectedAvailability,
@@ -2653,13 +2685,13 @@ export function AdminChangeBookingFlow({
2653
2685
  return false;
2654
2686
  }
2655
2687
  if (!selectedDate || selectedDate !== initialDay) return false;
2656
- const selectedOpt = normalizeProductOptionIdForChangeFlow(selectedAvailability.productOptionId);
2688
+ const selectedOpt = normalizeProductOptionIdForChangeFlow(getAvailabilityOptionId(selectedAvailability));
2657
2689
  const initialOpt = changeFlowResolvedInitialProductOptionId;
2658
2690
  if (selectedOpt != null && initialOpt != null) {
2659
2691
  return selectedOpt === initialOpt;
2660
2692
  }
2661
2693
  const legacyInitial = initialValues.productOptionId?.trim() || null;
2662
- const legacySelected = selectedAvailability.productOptionId?.trim() || null;
2694
+ const legacySelected = getAvailabilityOptionId(selectedAvailability) || null;
2663
2695
  return Boolean(legacySelected && legacyInitial && legacySelected === legacyInitial);
2664
2696
  }, [
2665
2697
  selectedDate,
@@ -2856,7 +2888,9 @@ export function AdminChangeBookingFlow({
2856
2888
  // Price breakdown: mid-layer returns line items (base + one per rule/deal). UI renders each line; rate in brackets when used.
2857
2889
  const getPriceBreakdown = useCallback((category: string, priceCAD: number, baseInDisplayCurrency: number | undefined, appliedAdjustments: Array<{ type: string; id: string; name: string; changeByCurrency?: Record<string, number> }> = []): PriceBreakdownData | null => {
2858
2890
  if (!pricingConfig) return null;
2859
- const selectedOption = activeOptions.find(opt => opt.optionId === selectedAvailability?.productOptionId);
2891
+ const selectedOption = activeOptions.find(
2892
+ opt => opt.optionId === (selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : '')
2893
+ );
2860
2894
  const basePriceCAD = selectedOption?.pricing?.[category.toUpperCase()] ?? 0;
2861
2895
  const isPublicMode = isSimplifiedPricingView;
2862
2896
  return computePriceBreakdown(
@@ -3332,7 +3366,7 @@ export function AdminChangeBookingFlow({
3332
3366
  if (!appliedPromoCode || !selectedAvailability || totalQuantity === 0) return '';
3333
3367
  const companyId = product.companyId ?? env.COMPANY_ID;
3334
3368
  if (!companyId) return '';
3335
- const optionId = selectedAvailability.productOptionId;
3369
+ const optionId = getAvailabilityOptionId(selectedAvailability);
3336
3370
  if (!optionId || !quantitiesSignature) return '';
3337
3371
  return [
3338
3372
  appliedPromoCode,
@@ -3349,7 +3383,7 @@ export function AdminChangeBookingFlow({
3349
3383
  }, [
3350
3384
  appliedPromoCode,
3351
3385
  selectedAvailability?.dateTime,
3352
- selectedAvailability?.productOptionId,
3386
+ selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : '',
3353
3387
  selectedAvailability?.availabilityId,
3354
3388
  totalQuantity,
3355
3389
  product.companyId,
@@ -3392,7 +3426,7 @@ export function AdminChangeBookingFlow({
3392
3426
  } = promoDiscountParamsRef.current;
3393
3427
  if (!code || !sel) return;
3394
3428
  const companyId = product.companyId ?? env.COMPANY_ID;
3395
- const optionId = sel.productOptionId;
3429
+ const optionId = getAvailabilityOptionId(sel);
3396
3430
  if (!companyId || !optionId) return;
3397
3431
  const items = lines.map((l) => ({ category: l.category, qty: l.qty }));
3398
3432
  if (items.length === 0) return;
@@ -3792,7 +3826,7 @@ export function AdminChangeBookingFlow({
3792
3826
  const initialOpt =
3793
3827
  changeFlowResolvedInitialProductOptionId ??
3794
3828
  (initialValues.productOptionId?.trim() || null);
3795
- const selectedOpt = normalizeProductOptionIdForChangeFlow(selectedAvailability.productOptionId);
3829
+ const selectedOpt = normalizeProductOptionIdForChangeFlow(getAvailabilityOptionId(selectedAvailability));
3796
3830
  const optionChanged = Boolean(
3797
3831
  selectedOpt != null && initialOpt != null && initialOpt !== selectedOpt
3798
3832
  );
@@ -3923,7 +3957,7 @@ export function AdminChangeBookingFlow({
3923
3957
  bookingReference: initialValues?.bookingReference?.trim() ?? '',
3924
3958
  lastName: lastName.trim().toLowerCase(),
3925
3959
  parentProductId: product.productId,
3926
- optionId: selectedAvailability?.productOptionId?.trim() || activeOptions[0]?.optionId || '',
3960
+ optionId: selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : '',
3927
3961
  dateTime: selectedAvailability?.dateTime ?? '',
3928
3962
  availabilityId: selectedAvailability?.availabilityId ?? null,
3929
3963
  pickupLocationId: pickupLocationId ?? null,
@@ -3938,7 +3972,7 @@ export function AdminChangeBookingFlow({
3938
3972
  initialValues?.bookingReference,
3939
3973
  lastName,
3940
3974
  product.productId,
3941
- selectedAvailability?.productOptionId,
3975
+ selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : '',
3942
3976
  selectedAvailability?.dateTime,
3943
3977
  selectedAvailability?.availabilityId,
3944
3978
  activeOptions,
@@ -4496,8 +4530,7 @@ export function AdminChangeBookingFlow({
4496
4530
  return;
4497
4531
  }
4498
4532
 
4499
- const optionId =
4500
- selectedAvailability.productOptionId?.trim() || activeOptions[0]?.optionId;
4533
+ const optionId = getAvailabilityOptionId(selectedAvailability);
4501
4534
  if (!optionId) {
4502
4535
  setLatestChangeQuote(null);
4503
4536
  setChangeQuoteLoading(false);
@@ -4595,7 +4628,7 @@ export function AdminChangeBookingFlow({
4595
4628
  hasChangeSelection,
4596
4629
  selectedAvailability,
4597
4630
  selectedAvailability?.dateTime,
4598
- selectedAvailability?.productOptionId,
4631
+ selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : '',
4599
4632
  initialValues?.bookingReference,
4600
4633
  lastName,
4601
4634
  missingRequiredReturnSelection,
@@ -4661,7 +4694,7 @@ export function AdminChangeBookingFlow({
4661
4694
 
4662
4695
  const mostPopularOption = activeOptions.find(opt => opt.mostPopular);
4663
4696
  const candidate = mostPopularOption
4664
- ? timesForSelectedDate.find(avail => avail.productOptionId === mostPopularOption.optionId && avail.vacancies > 0)
4697
+ ? timesForSelectedDate.find(avail => getAvailabilityOptionId(avail) === mostPopularOption.optionId && avail.vacancies > 0)
4665
4698
  : null;
4666
4699
  const fallback = timesForSelectedDate.find(avail => avail.vacancies > 0);
4667
4700
  const toSelect = candidate ?? fallback;
@@ -4687,9 +4720,9 @@ export function AdminChangeBookingFlow({
4687
4720
  useEffect(() => {
4688
4721
  if (selectedAvailability && availabilities.length > 0) {
4689
4722
  const updatedAvailability = availabilities.find(
4690
- avail =>
4723
+ avail =>
4691
4724
  avail.dateTime === selectedAvailability.dateTime &&
4692
- avail.productOptionId === selectedAvailability.productOptionId
4725
+ getAvailabilityOptionId(avail) === getAvailabilityOptionId(selectedAvailability)
4693
4726
  );
4694
4727
  if (updatedAvailability) {
4695
4728
  setSelectedAvailability(updatedAvailability);
@@ -4720,7 +4753,7 @@ export function AdminChangeBookingFlow({
4720
4753
  setImplicitReturnBaselineId(null);
4721
4754
  }, [
4722
4755
  selectedAvailability?.dateTime,
4723
- selectedAvailability?.productOptionId,
4756
+ selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : '',
4724
4757
  initialValues?.returnAvailabilityId,
4725
4758
  initialValues?.returnDateTime,
4726
4759
  ]);
@@ -4808,7 +4841,7 @@ export function AdminChangeBookingFlow({
4808
4841
  }, [selectedAvailability?.returnOptions, selectedReturnOption]);
4809
4842
 
4810
4843
  // Fetch add-ons when availability (product option) is selected; clear selections when option changes
4811
- const availabilityProductOptionId = selectedAvailability?.productOptionId ?? null;
4844
+ const availabilityProductOptionId = selectedAvailability ? getAvailabilityOptionId(selectedAvailability) || null : null;
4812
4845
  const prevAvailabilityProductOptionIdRef = useRef<string | null>(null);
4813
4846
  useEffect(() => {
4814
4847
  if (!availabilityProductOptionId || !product.companyId) {
@@ -4828,7 +4861,7 @@ export function AdminChangeBookingFlow({
4828
4861
  if (date === selectedDate) return;
4829
4862
  if (selectedAvailability) {
4830
4863
  changeFlowOutboundAnchorRef.current = {
4831
- productOptionId: selectedAvailability.productOptionId ?? null,
4864
+ productOptionId: getAvailabilityOptionId(selectedAvailability) || null,
4832
4865
  minutesFromMidnight: getMinutesFromMidnightInTimezone(
4833
4866
  selectedAvailability.dateTime,
4834
4867
  companyTimezone
@@ -4965,8 +4998,8 @@ export function AdminChangeBookingFlow({
4965
4998
  [selectedAvailability]
4966
4999
  );
4967
5000
  const selectedAvailabilityKey = useMemo(
4968
- () => `${selectedAvailability?.dateTime ?? ''}::${selectedAvailability?.productOptionId ?? ''}`,
4969
- [selectedAvailability?.dateTime, selectedAvailability?.productOptionId]
5001
+ () => `${selectedAvailability?.dateTime ?? ''}::${selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : ''}`,
5002
+ [selectedAvailability]
4970
5003
  );
4971
5004
  // Remember where promo was successfully applied to avoid self-clearing on same selection.
4972
5005
  const promoAppliedSelectionKeyRef = useRef<string | null>(null);
@@ -5121,8 +5154,7 @@ export function AdminChangeBookingFlow({
5121
5154
  .map(([category, count]) => ({ category, count }));
5122
5155
 
5123
5156
  // Get the productOptionId from the selected availability (we tagged it when fetching)
5124
- const availabilityProductOptionId = selectedAvailability.productOptionId
5125
- || activeOptions[0]?.optionId;
5157
+ const availabilityProductOptionId = getAvailabilityOptionId(selectedAvailability);
5126
5158
 
5127
5159
  if (!availabilityProductOptionId) {
5128
5160
  setError('No product option selected');
@@ -5657,8 +5689,7 @@ export function AdminChangeBookingFlow({
5657
5689
  .map(([category, count]) => ({ category, count }));
5658
5690
  const availabilityProductOptionId =
5659
5691
  adminChoiceData.availabilityProductOptionId ||
5660
- selectedAvailability.productOptionId ||
5661
- activeOptions[0]?.optionId;
5692
+ getAvailabilityOptionId(selectedAvailability);
5662
5693
  if (!availabilityProductOptionId) {
5663
5694
  setError('No product option selected');
5664
5695
  setLoading(false);