@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.
@@ -263,18 +263,30 @@ function findMergedAvailabilityForSelection(
263
263
  selected: Availability | null
264
264
  ): Availability | undefined {
265
265
  if (!selected) return undefined;
266
- const optId = selected.productOptionId ?? undefined;
266
+ const optId = getAvailabilityOptionId(selected) || undefined;
267
267
  const dt = selected.dateTime;
268
268
  const availId = selected.availabilityId?.trim();
269
269
  if (availId && optId) {
270
270
  const exact = merged.find(
271
271
  (a) =>
272
272
  a.availabilityId === availId &&
273
- (a.productOptionId === optId || a.productOptionId === undefined)
273
+ getAvailabilityOptionId(a) === optId
274
274
  );
275
275
  if (exact) return exact;
276
276
  }
277
- return merged.find((a) => a.dateTime === dt && a.productOptionId === optId);
277
+ return merged.find((a) => a.dateTime === dt && getAvailabilityOptionId(a) === optId);
278
+ }
279
+
280
+ function getAvailabilityOptionId(availability: Availability, fallbackOptionId?: string): string {
281
+ const productOptionId = availability.productOptionId?.trim();
282
+ if (productOptionId) return productOptionId;
283
+ const productId = availability.productId?.trim();
284
+ if (productId?.startsWith('po_')) return productId;
285
+ return fallbackOptionId?.trim() ?? '';
286
+ }
287
+
288
+ function getAvailabilityCacheKey(availability: Availability): string {
289
+ return `${availability.dateTime}-${getAvailabilityOptionId(availability)}`;
278
290
  }
279
291
 
280
292
  /** Ticket rates for one availability row — mirrors main cart [pricing] useMemo so baseline slots match BE. */
@@ -288,7 +300,7 @@ function buildPricingFromAvailability(
288
300
  isSimplifiedPricingView: boolean,
289
301
  ) {
290
302
  if (!selectedAvailability || !pricingConfig) return [];
291
- const optionId = selectedAvailability.productOptionId;
303
+ const optionId = getAvailabilityOptionId(selectedAvailability);
292
304
  const selectedOption = activeOptions.find((opt) => opt.optionId === optionId);
293
305
  const precomputed = optionId ? precomputedPricesByOption?.[optionId] : undefined;
294
306
  const rateToDisplayPrice = (backendInDisplayCurrency: number) =>
@@ -306,11 +318,17 @@ function buildPricingFromAvailability(
306
318
  appliedAdjustments: Array<{ type: string; id: string; name: string; changeByCurrency?: Record<string, number> }>,
307
319
  ) => {
308
320
  const basePriceCAD = selectedOption?.pricing?.[category.toUpperCase()] ?? 0;
321
+ const hasOptionScopedPrice = baseInDisplayCurrency > 0;
322
+ const priceCADForBreakdown =
323
+ hasOptionScopedPrice && currency === 'CAD' ? baseInDisplayCurrency : backendPriceCAD;
324
+ const displayCurrencyPriceForFallback = hasOptionScopedPrice
325
+ ? baseInDisplayCurrency
326
+ : backendInDisplayCurrency;
309
327
  const isPublicMode = isSimplifiedPricingView;
310
328
  const breakdown = computePriceBreakdown(
311
329
  pricingConfig,
312
330
  currency,
313
- backendPriceCAD,
331
+ priceCADForBreakdown,
314
332
  basePriceCAD,
315
333
  hasFees,
316
334
  appliedAdjustments,
@@ -318,8 +336,8 @@ function buildPricingFromAvailability(
318
336
  baseInDisplayCurrency,
319
337
  isPublicMode,
320
338
  );
321
- const price = breakdown?.finalPrice ?? rateToDisplayPrice(backendInDisplayCurrency);
322
- return { category, baseInDisplayCurrency, appliedAdjustments, price, priceCAD: backendPriceCAD };
339
+ const price = breakdown?.finalPrice ?? rateToDisplayPrice(displayCurrencyPriceForFallback);
340
+ return { category, baseInDisplayCurrency, appliedAdjustments, price, priceCAD: priceCADForBreakdown };
323
341
  };
324
342
  return (
325
343
  selectedAvailability.rates?.map((rate) => {
@@ -770,6 +788,14 @@ export function ChangeBookingFlow({
770
788
  return '';
771
789
  }
772
790
  });
791
+ const selectedDateRangeExtensionKey = useMemo(() => {
792
+ if (!visibleRange || !selectedDate) return '';
793
+ try {
794
+ return isAfter(parseISO(selectedDate), visibleRange.end) ? selectedDate : '';
795
+ } catch {
796
+ return '';
797
+ }
798
+ }, [selectedDate, visibleRange]);
773
799
  const [isItinerarySticky, setIsItinerarySticky] = useState(false);
774
800
  const isItineraryStickyRef = useRef(false);
775
801
  const [isMobile, setIsMobile] = useState(false);
@@ -980,6 +1006,7 @@ export function ChangeBookingFlow({
980
1006
 
981
1007
  const availabilityPromises = activeOptions.map(async (option) => {
982
1008
  const result = await getAvailabilities(option.optionId, startDateStr, endDateStr, {
1009
+ ...(!isPrivateShuttle ? { responseMode: 'booking-day' as const } : {}),
983
1010
  promoCode: appliedPromoCode || undefined,
984
1011
  ...(pricingProfileIdForAvailabilities
985
1012
  ? { pricingProfileId: pricingProfileIdForAvailabilities }
@@ -1024,10 +1051,10 @@ export function ChangeBookingFlow({
1024
1051
  let mergedOut: Availability[] = [];
1025
1052
  setAvailabilities((prev) => {
1026
1053
  const existingMap = new Map(
1027
- prev.map((avail) => [`${avail.dateTime}-${avail.productOptionId}`, avail])
1054
+ prev.map((avail) => [getAvailabilityCacheKey(avail), avail])
1028
1055
  );
1029
1056
  allFetchedAvailabilities.forEach((avail) => {
1030
- existingMap.set(`${avail.dateTime}-${avail.productOptionId}`, avail);
1057
+ existingMap.set(getAvailabilityCacheKey(avail), avail);
1031
1058
  });
1032
1059
  mergedOut = Array.from(existingMap.values());
1033
1060
  return mergedOut;
@@ -1054,16 +1081,17 @@ export function ChangeBookingFlow({
1054
1081
  activeOptionIdsKey,
1055
1082
  appliedPromoCode,
1056
1083
  pricingProfileIdForAvailabilities,
1084
+ cancellationPolicyProfileIdForAvailabilities,
1057
1085
  )
1058
1086
  : null;
1059
1087
  if (cacheKey && availabilitiesCache) {
1060
1088
  const existingCache = availabilitiesCache.get(cacheKey);
1061
1089
  const existingAvailabilities = existingCache?.availabilities ?? [];
1062
1090
  const mergedAvailabilitiesMap = new Map(
1063
- existingAvailabilities.map((a) => [`${a.dateTime}-${a.productOptionId}`, a])
1091
+ existingAvailabilities.map((a) => [getAvailabilityCacheKey(a), a])
1064
1092
  );
1065
1093
  allFetchedAvailabilities.forEach((a) => {
1066
- mergedAvailabilitiesMap.set(`${a.dateTime}-${a.productOptionId}`, a);
1094
+ mergedAvailabilitiesMap.set(getAvailabilityCacheKey(a), a);
1067
1095
  });
1068
1096
  const mergedPrecomputed = { ...(existingCache?.precomputedPricesByOption ?? {}) };
1069
1097
  results.forEach((r) => {
@@ -1178,11 +1206,11 @@ export function ChangeBookingFlow({
1178
1206
  ? endOfLatestDay
1179
1207
  : visibleRange.end;
1180
1208
 
1181
- // Ensure we include the selected date if it's after the visible range end
1182
- // This handles the case where user selects a date that's not yet in the visible range
1183
- if (selectedDate) {
1209
+ // Include an externally seeded selected date only when it falls outside the current calendar window.
1210
+ // Normal date clicks are already inside visibleRange and should not re-fetch the broad availability range.
1211
+ if (selectedDateRangeExtensionKey) {
1184
1212
  try {
1185
- const selectedDateObj = parseISO(selectedDate);
1213
+ const selectedDateObj = parseISO(selectedDateRangeExtensionKey);
1186
1214
  if (isAfter(selectedDateObj, clampedEnd)) {
1187
1215
  clampedEnd = selectedDateObj;
1188
1216
  }
@@ -1198,6 +1226,7 @@ export function ChangeBookingFlow({
1198
1226
  activeOptionIdsKey,
1199
1227
  appliedPromoCode,
1200
1228
  pricingProfileIdForAvailabilities,
1229
+ cancellationPolicyProfileIdForAvailabilities,
1201
1230
  )
1202
1231
  : null;
1203
1232
  const cached = cacheKey ? availabilitiesCache!.get(cacheKey) : undefined;
@@ -1291,6 +1320,7 @@ export function ChangeBookingFlow({
1291
1320
  // Fetch availabilities for all product options in parallel (no currency param - we use precomputedPrices for display)
1292
1321
  const availabilityPromises = activeOptions.map(async (option) => {
1293
1322
  const result = await getAvailabilities(option.optionId, startDateStr, endDateStr, {
1323
+ ...(!isPrivateShuttle ? { responseMode: 'booking-day' as const } : {}),
1294
1324
  promoCode: appliedPromoCode || undefined,
1295
1325
  ...(pricingProfileIdForAvailabilities
1296
1326
  ? { pricingProfileId: pricingProfileIdForAvailabilities }
@@ -1339,12 +1369,12 @@ export function ChangeBookingFlow({
1339
1369
  // Merge with existing availabilities (avoid duplicates by dateTime + productOptionId)
1340
1370
  setAvailabilities(prev => {
1341
1371
  const existingMap = new Map(
1342
- prev.map(avail => [`${avail.dateTime}-${avail.productOptionId}`, avail])
1372
+ prev.map(avail => [getAvailabilityCacheKey(avail), avail])
1343
1373
  );
1344
1374
 
1345
1375
  // Merge new availabilities - update existing ones or add new ones
1346
1376
  allFetchedAvailabilities.forEach(avail => {
1347
- const key = `${avail.dateTime}-${avail.productOptionId}`;
1377
+ const key = getAvailabilityCacheKey(avail);
1348
1378
  // Always update to get latest data (vacancies, prices, etc.)
1349
1379
  existingMap.set(key, avail);
1350
1380
  });
@@ -1373,10 +1403,10 @@ export function ChangeBookingFlow({
1373
1403
  const existingCache = availabilitiesCache.get(cacheKey);
1374
1404
  const existingAvailabilities = existingCache?.availabilities ?? [];
1375
1405
  const mergedAvailabilitiesMap = new Map(
1376
- existingAvailabilities.map((a) => [`${a.dateTime}-${a.productOptionId}`, a])
1406
+ existingAvailabilities.map((a) => [getAvailabilityCacheKey(a), a])
1377
1407
  );
1378
1408
  allFetchedAvailabilities.forEach((a) => {
1379
- mergedAvailabilitiesMap.set(`${a.dateTime}-${a.productOptionId}`, a);
1409
+ mergedAvailabilitiesMap.set(getAvailabilityCacheKey(a), a);
1380
1410
  });
1381
1411
  const mergedPrecomputed = { ...(existingCache?.precomputedPricesByOption ?? {}) };
1382
1412
  results.forEach((r) => {
@@ -1424,7 +1454,7 @@ export function ChangeBookingFlow({
1424
1454
  activeOptionIdsKey,
1425
1455
  isPrivateShuttle,
1426
1456
  companyTimezone,
1427
- selectedDate,
1457
+ selectedDateRangeExtensionKey,
1428
1458
  appliedPromoCode,
1429
1459
  pricingProfileIdForAvailabilities,
1430
1460
  cancellationPolicyProfileIdForAvailabilities,
@@ -1622,7 +1652,7 @@ export function ChangeBookingFlow({
1622
1652
  timesForSelectedDate
1623
1653
  .map(
1624
1654
  (avail) =>
1625
- `${avail.dateTime}|${avail.productOptionId ?? ''}|${avail.vacancies ?? 0}`,
1655
+ `${avail.dateTime}|${getAvailabilityOptionId(avail)}|${avail.vacancies ?? 0}`,
1626
1656
  )
1627
1657
  .join('||'),
1628
1658
  [timesForSelectedDate],
@@ -2054,7 +2084,9 @@ export function ChangeBookingFlow({
2054
2084
  const calculateStaySummary = useCallback((returnDateTime: Date): string | null => {
2055
2085
  if (!selectedAvailability) return null;
2056
2086
 
2057
- const availabilityProductOptionId = selectedAvailability.productOptionId || activeOptions[0]?.optionId;
2087
+ const availabilityProductOptionId =
2088
+ getAvailabilityOptionId(selectedAvailability) ||
2089
+ (activeOptions.length === 1 ? activeOptions[0]?.optionId : undefined);
2058
2090
  const selectedOption = activeOptions.find(opt => opt.optionId === availabilityProductOptionId);
2059
2091
  if (!selectedOption) return null;
2060
2092
 
@@ -2130,7 +2162,9 @@ export function ChangeBookingFlow({
2130
2162
  // Helper function to compute itinerary display for storage (returns same shape as "Your Itinerary" box)
2131
2163
  const computeItineraryDisplay = useCallback((): ItineraryDisplayStep[] | null => {
2132
2164
  if (!selectedAvailability) return null;
2133
- const availabilityProductOptionId = selectedAvailability.productOptionId || activeOptions[0]?.optionId;
2165
+ const availabilityProductOptionId =
2166
+ getAvailabilityOptionId(selectedAvailability) ||
2167
+ (activeOptions.length === 1 ? activeOptions[0]?.optionId : undefined);
2134
2168
  const selectedOption = activeOptions.find(opt => opt.optionId === availabilityProductOptionId);
2135
2169
  if (!selectedOption) return null;
2136
2170
  const tourStartTime = parseISO(selectedAvailability.dateTime);
@@ -2369,13 +2403,13 @@ export function ChangeBookingFlow({
2369
2403
  const selectedMs = parseAvailabilityDateTime(selectedAvailability.dateTime).getTime();
2370
2404
  const initialMs = parseAvailabilityDateTime(initialValues.dateTime).getTime();
2371
2405
  if (selectedMs !== initialMs) return false;
2372
- const selectedOpt = normalizeProductOptionIdForChangeFlow(selectedAvailability.productOptionId);
2406
+ const selectedOpt = normalizeProductOptionIdForChangeFlow(getAvailabilityOptionId(selectedAvailability));
2373
2407
  const initialOpt = changeFlowResolvedInitialProductOptionId;
2374
2408
  if (selectedOpt != null && initialOpt != null) {
2375
2409
  return selectedOpt === initialOpt;
2376
2410
  }
2377
2411
  const legacyInitial = initialValues.productOptionId?.trim() || null;
2378
- const legacySelected = selectedAvailability.productOptionId?.trim() || null;
2412
+ const legacySelected = getAvailabilityOptionId(selectedAvailability) || null;
2379
2413
  return !legacySelected || !legacyInitial || legacySelected === legacyInitial;
2380
2414
  }, [
2381
2415
  selectedAvailability,
@@ -2402,13 +2436,13 @@ export function ChangeBookingFlow({
2402
2436
  return false;
2403
2437
  }
2404
2438
  if (!selectedDate || selectedDate !== initialDay) return false;
2405
- const selectedOpt = normalizeProductOptionIdForChangeFlow(selectedAvailability.productOptionId);
2439
+ const selectedOpt = normalizeProductOptionIdForChangeFlow(getAvailabilityOptionId(selectedAvailability));
2406
2440
  const initialOpt = changeFlowResolvedInitialProductOptionId;
2407
2441
  if (selectedOpt != null && initialOpt != null) {
2408
2442
  return selectedOpt === initialOpt;
2409
2443
  }
2410
2444
  const legacyInitial = initialValues.productOptionId?.trim() || null;
2411
- const legacySelected = selectedAvailability.productOptionId?.trim() || null;
2445
+ const legacySelected = getAvailabilityOptionId(selectedAvailability) || null;
2412
2446
  return Boolean(legacySelected && legacyInitial && legacySelected === legacyInitial);
2413
2447
  }, [
2414
2448
  selectedDate,
@@ -2605,7 +2639,9 @@ export function ChangeBookingFlow({
2605
2639
  // Price breakdown: mid-layer returns line items (base + one per rule/deal). UI renders each line; rate in brackets when used.
2606
2640
  const getPriceBreakdown = useCallback((category: string, priceCAD: number, baseInDisplayCurrency: number | undefined, appliedAdjustments: Array<{ type: string; id: string; name: string; changeByCurrency?: Record<string, number> }> = []): PriceBreakdownData | null => {
2607
2641
  if (!pricingConfig) return null;
2608
- const selectedOption = activeOptions.find(opt => opt.optionId === selectedAvailability?.productOptionId);
2642
+ const selectedOption = activeOptions.find(
2643
+ opt => opt.optionId === (selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : '')
2644
+ );
2609
2645
  const basePriceCAD = selectedOption?.pricing?.[category.toUpperCase()] ?? 0;
2610
2646
  const isPublicMode = isSimplifiedPricingView;
2611
2647
  return computePriceBreakdown(
@@ -3037,7 +3073,7 @@ export function ChangeBookingFlow({
3037
3073
  if (!appliedPromoCode || !selectedAvailability || totalQuantity === 0) return '';
3038
3074
  const companyId = product.companyId ?? env.COMPANY_ID;
3039
3075
  if (!companyId) return '';
3040
- const optionId = selectedAvailability.productOptionId;
3076
+ const optionId = getAvailabilityOptionId(selectedAvailability);
3041
3077
  if (!optionId || !quantitiesSignature) return '';
3042
3078
  return [
3043
3079
  appliedPromoCode,
@@ -3054,7 +3090,7 @@ export function ChangeBookingFlow({
3054
3090
  }, [
3055
3091
  appliedPromoCode,
3056
3092
  selectedAvailability?.dateTime,
3057
- selectedAvailability?.productOptionId,
3093
+ selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : '',
3058
3094
  selectedAvailability?.availabilityId,
3059
3095
  totalQuantity,
3060
3096
  product.companyId,
@@ -3097,7 +3133,7 @@ export function ChangeBookingFlow({
3097
3133
  } = promoDiscountParamsRef.current;
3098
3134
  if (!code || !sel) return;
3099
3135
  const companyId = product.companyId ?? env.COMPANY_ID;
3100
- const optionId = sel.productOptionId;
3136
+ const optionId = getAvailabilityOptionId(sel);
3101
3137
  if (!companyId || !optionId) return;
3102
3138
  const items = lines.map((l) => ({ category: l.category, qty: l.qty }));
3103
3139
  if (items.length === 0) return;
@@ -3240,7 +3276,7 @@ export function ChangeBookingFlow({
3240
3276
  const initialOpt =
3241
3277
  changeFlowResolvedInitialProductOptionId ??
3242
3278
  (initialValues.productOptionId?.trim() || null);
3243
- const selectedOpt = normalizeProductOptionIdForChangeFlow(selectedAvailability.productOptionId);
3279
+ const selectedOpt = normalizeProductOptionIdForChangeFlow(getAvailabilityOptionId(selectedAvailability));
3244
3280
  const optionChanged = Boolean(
3245
3281
  selectedOpt != null && initialOpt != null && initialOpt !== selectedOpt
3246
3282
  );
@@ -3608,8 +3644,7 @@ export function ChangeBookingFlow({
3608
3644
  return;
3609
3645
  }
3610
3646
 
3611
- const optionId =
3612
- selectedAvailability.productOptionId?.trim() || activeOptions[0]?.optionId;
3647
+ const optionId = getAvailabilityOptionId(selectedAvailability);
3613
3648
  if (!optionId) {
3614
3649
  setLatestChangeQuote(null);
3615
3650
  setChangeQuoteLoading(false);
@@ -3683,7 +3718,7 @@ export function ChangeBookingFlow({
3683
3718
  hasChangeSelection,
3684
3719
  selectedAvailability,
3685
3720
  selectedAvailability?.dateTime,
3686
- selectedAvailability?.productOptionId,
3721
+ selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : '',
3687
3722
  initialValues?.bookingReference,
3688
3723
  lastName,
3689
3724
  missingRequiredReturnSelection,
@@ -3742,7 +3777,7 @@ export function ChangeBookingFlow({
3742
3777
 
3743
3778
  const mostPopularOption = activeOptions.find(opt => opt.mostPopular);
3744
3779
  const candidate = mostPopularOption
3745
- ? timesForSelectedDate.find(avail => avail.productOptionId === mostPopularOption.optionId && avail.vacancies > 0)
3780
+ ? timesForSelectedDate.find(avail => getAvailabilityOptionId(avail) === mostPopularOption.optionId && avail.vacancies > 0)
3746
3781
  : null;
3747
3782
  const fallback = timesForSelectedDate.find(avail => avail.vacancies > 0);
3748
3783
  const toSelect = candidate ?? fallback;
@@ -3768,9 +3803,9 @@ export function ChangeBookingFlow({
3768
3803
  useEffect(() => {
3769
3804
  if (selectedAvailability && availabilities.length > 0) {
3770
3805
  const updatedAvailability = availabilities.find(
3771
- avail =>
3806
+ avail =>
3772
3807
  avail.dateTime === selectedAvailability.dateTime &&
3773
- avail.productOptionId === selectedAvailability.productOptionId
3808
+ getAvailabilityOptionId(avail) === getAvailabilityOptionId(selectedAvailability)
3774
3809
  );
3775
3810
  if (updatedAvailability) {
3776
3811
  setSelectedAvailability(updatedAvailability);
@@ -3801,7 +3836,7 @@ export function ChangeBookingFlow({
3801
3836
  setImplicitReturnBaselineId(null);
3802
3837
  }, [
3803
3838
  selectedAvailability?.dateTime,
3804
- selectedAvailability?.productOptionId,
3839
+ selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : '',
3805
3840
  initialValues?.returnAvailabilityId,
3806
3841
  initialValues?.returnDateTime,
3807
3842
  ]);
@@ -3873,7 +3908,7 @@ export function ChangeBookingFlow({
3873
3908
  ]);
3874
3909
 
3875
3910
  // Fetch add-ons when availability (product option) is selected; clear selections when option changes
3876
- const availabilityProductOptionId = selectedAvailability?.productOptionId ?? null;
3911
+ const availabilityProductOptionId = selectedAvailability ? getAvailabilityOptionId(selectedAvailability) || null : null;
3877
3912
  const prevAvailabilityProductOptionIdRef = useRef<string | null>(null);
3878
3913
  useEffect(() => {
3879
3914
  if (!availabilityProductOptionId || !product.companyId) {
@@ -3893,7 +3928,7 @@ export function ChangeBookingFlow({
3893
3928
  if (date === selectedDate) return;
3894
3929
  if (selectedAvailability) {
3895
3930
  changeFlowOutboundAnchorRef.current = {
3896
- productOptionId: selectedAvailability.productOptionId ?? null,
3931
+ productOptionId: getAvailabilityOptionId(selectedAvailability) || null,
3897
3932
  minutesFromMidnight: getMinutesFromMidnightInTimezone(
3898
3933
  selectedAvailability.dateTime,
3899
3934
  companyTimezone
@@ -4030,8 +4065,8 @@ export function ChangeBookingFlow({
4030
4065
  [selectedAvailability]
4031
4066
  );
4032
4067
  const selectedAvailabilityKey = useMemo(
4033
- () => `${selectedAvailability?.dateTime ?? ''}::${selectedAvailability?.productOptionId ?? ''}`,
4034
- [selectedAvailability?.dateTime, selectedAvailability?.productOptionId]
4068
+ () => `${selectedAvailability?.dateTime ?? ''}::${selectedAvailability ? getAvailabilityOptionId(selectedAvailability) : ''}`,
4069
+ [selectedAvailability]
4035
4070
  );
4036
4071
  // Remember where promo was successfully applied to avoid self-clearing on same selection.
4037
4072
  const promoAppliedSelectionKeyRef = useRef<string | null>(null);
@@ -4183,8 +4218,7 @@ export function ChangeBookingFlow({
4183
4218
  .map(([category, count]) => ({ category, count }));
4184
4219
 
4185
4220
  // Get the productOptionId from the selected availability (we tagged it when fetching)
4186
- const availabilityProductOptionId = selectedAvailability.productOptionId
4187
- || activeOptions[0]?.optionId;
4221
+ const availabilityProductOptionId = getAvailabilityOptionId(selectedAvailability);
4188
4222
 
4189
4223
  if (!availabilityProductOptionId) {
4190
4224
  setError('No product option selected');