@ticketboothapp/booking 1.2.98 → 1.2.100

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.
@@ -28,6 +28,7 @@ import {
28
28
  EARLIEST_AVAILABILITY_DATE,
29
29
  LATEST_AVAILABILITY_DATE,
30
30
  INITIAL_FETCH_WEEKS,
31
+ VISIBLE_RANGE_BUFFER_WEEKS,
31
32
  } from '../../lib/booking-constants';
32
33
  import { formatCurrencyAmount } from '../../lib/currency';
33
34
  import { formatBookingRefForDisplay } from '../../lib/booking-ref';
@@ -447,11 +448,86 @@ export function PrivateShuttleBookingFlow({
447
448
  availabilitiesCache,
448
449
  ]);
449
450
 
451
+ /** Fresh selected-day check before reserve so stale private shuttle vacancies are corrected before payment. */
452
+ const refreshSelectedDateDetailsForCheckout = useCallback(async (): Promise<Availability[]> => {
453
+ if (!selectedDate || !product.productId) return [];
454
+
455
+ const result = await getAvailabilities(product.productId, selectedDate, selectedDate, {
456
+ allOptions: true,
457
+ promoCode: activePromoCode || undefined,
458
+ ...(pricingProfileIdForAvailabilities
459
+ ? { pricingProfileId: pricingProfileIdForAvailabilities }
460
+ : {}),
461
+ ...(cancellationPolicyProfileIdForAvailabilities
462
+ ? { cancellationPolicyProfileId: cancellationPolicyProfileIdForAvailabilities }
463
+ : {}),
464
+ });
465
+
466
+ if (result.pricingConfig && !pricingConfigSetRef.current) {
467
+ setPricingConfig(result.pricingConfig);
468
+ pricingConfigSetRef.current = true;
469
+ }
470
+ if (result.precomputedPrices) setPrecomputedPrices(result.precomputedPrices);
471
+ if (result.resourcePriceByCurrency) setResourcePriceByCurrency(result.resourcePriceByCurrency);
472
+ if (result.resourcePriceByOption) setResourcePriceByOption(result.resourcePriceByOption);
473
+
474
+ let mergedAvailabilities: Availability[] = [];
475
+ setAvailabilities((prev) => {
476
+ const existingMap = new Map(
477
+ prev.map((avail) => [`${avail.dateTime}-${avail.productId || avail.productOptionId}`, avail])
478
+ );
479
+ result.availabilities.forEach((avail) => {
480
+ existingMap.set(`${avail.dateTime}-${avail.productId || avail.productOptionId}`, avail);
481
+ });
482
+ mergedAvailabilities = Array.from(existingMap.values());
483
+ return mergedAvailabilities;
484
+ });
485
+
486
+ const cacheKey = availabilitiesCache
487
+ ? buildAvailabilitiesCacheKey(
488
+ product.productId,
489
+ activeOptionIdsKey,
490
+ activePromoCode,
491
+ pricingProfileIdForAvailabilities,
492
+ )
493
+ : null;
494
+ if (cacheKey && availabilitiesCache) {
495
+ const existingCache = availabilitiesCache.get(cacheKey);
496
+ const mergedAvailabilitiesMap = new Map(
497
+ (existingCache?.availabilities ?? []).map((availability) => [
498
+ `${availability.dateTime}-${availability.productId || availability.productOptionId}`,
499
+ availability,
500
+ ])
501
+ );
502
+ result.availabilities.forEach((availability) => {
503
+ mergedAvailabilitiesMap.set(`${availability.dateTime}-${availability.productId || availability.productOptionId}`, availability);
504
+ });
505
+ availabilitiesCache.merge(cacheKey, {
506
+ fetchedRanges: existingCache?.fetchedRanges ?? fetchedRangesRef.current,
507
+ availabilities: Array.from(mergedAvailabilitiesMap.values()),
508
+ pricingConfig: result.pricingConfig ?? existingCache?.pricingConfig ?? null,
509
+ precomputedPrices: result.precomputedPrices ?? existingCache?.precomputedPrices ?? null,
510
+ resourcePriceByCurrency: result.resourcePriceByCurrency ?? existingCache?.resourcePriceByCurrency ?? null,
511
+ resourcePriceByOption: result.resourcePriceByOption ?? existingCache?.resourcePriceByOption ?? null,
512
+ });
513
+ }
514
+
515
+ return result.availabilities;
516
+ }, [
517
+ selectedDate,
518
+ product.productId,
519
+ activePromoCode,
520
+ pricingProfileIdForAvailabilities,
521
+ cancellationPolicyProfileIdForAvailabilities,
522
+ availabilitiesCache,
523
+ activeOptionIdsKey,
524
+ ]);
525
+
450
526
  useEffect(() => {
451
527
  if (!visibleRange) {
452
528
  setVisibleRange({
453
529
  start: EARLIEST_AVAILABILITY_DATE,
454
- end: addWeeks(EARLIEST_AVAILABILITY_DATE, INITIAL_FETCH_WEEKS),
530
+ end: addWeeks(EARLIEST_AVAILABILITY_DATE, INITIAL_FETCH_WEEKS + VISIBLE_RANGE_BUFFER_WEEKS),
455
531
  });
456
532
  }
457
533
  }, [visibleRange]);
@@ -534,6 +610,7 @@ export function PrivateShuttleBookingFlow({
534
610
  )
535
611
  : null;
536
612
  const cached = cacheKey ? availabilitiesCache!.get(cacheKey) : undefined;
613
+ let shouldRevalidateCachedRange = false;
537
614
  if (cached && cached.availabilities.length > 0) {
538
615
  const cacheCoversRange = cached.fetchedRanges.some(
539
616
  (r) =>
@@ -541,6 +618,7 @@ export function PrivateShuttleBookingFlow({
541
618
  );
542
619
  const isStale = availabilitiesCache?.isStale(cached) ?? false;
543
620
  if (cacheCoversRange) {
621
+ shouldRevalidateCachedRange = true;
544
622
  setAvailabilities(cached.availabilities);
545
623
  if (cached.pricingConfig) {
546
624
  setPricingConfig(cached.pricingConfig);
@@ -553,8 +631,7 @@ export function PrivateShuttleBookingFlow({
553
631
  setIsFetchingMoreAvailabilities(false);
554
632
  if (!isStale) {
555
633
  fetchedRangesRef.current = [...cached.fetchedRanges];
556
- fetchingRef.current = false;
557
- return;
634
+ // Show cached availability instantly, then quietly revalidate the visible range.
558
635
  }
559
636
  }
560
637
  // Partial cache: show cached data immediately, then fetch missing range below
@@ -570,7 +647,7 @@ export function PrivateShuttleBookingFlow({
570
647
  setLoadingAvailabilities(false);
571
648
  }
572
649
 
573
- if (!needsFetch(clampedStart, clampedEnd)) {
650
+ if (!shouldRevalidateCachedRange && !needsFetch(clampedStart, clampedEnd)) {
574
651
  setLoadingAvailabilities(false);
575
652
  setIsFetchingMoreAvailabilities(false);
576
653
  fetchingRef.current = false;
@@ -579,12 +656,13 @@ export function PrivateShuttleBookingFlow({
579
656
  const hasPartialCache = cached && cached.availabilities.length > 0;
580
657
  fetchingRef.current = true;
581
658
  if (!hasPartialCache) setLoadingAvailabilities(true);
582
- else setIsFetchingMoreAvailabilities(true);
659
+ else if (!shouldRevalidateCachedRange) setIsFetchingMoreAvailabilities(true);
583
660
  try {
584
661
  const startDate = format(startOfDay(clampedStart), 'yyyy-MM-dd');
585
662
  const endDate = format(endOfDay(clampedEnd), 'yyyy-MM-dd');
586
663
  const result = await getAvailabilities(product.productId, startDate, endDate, {
587
664
  allOptions: true,
665
+ summary: true,
588
666
  promoCode: activePromoCode || undefined,
589
667
  ...(pricingProfileIdForAvailabilities
590
668
  ? { pricingProfileId: pricingProfileIdForAvailabilities }
@@ -643,7 +721,11 @@ export function PrivateShuttleBookingFlow({
643
721
  });
644
722
  }
645
723
  } catch (err) {
646
- setError(err instanceof Error ? err.message : 'Failed to load availabilities');
724
+ if (shouldRevalidateCachedRange && cached?.availabilities.length) {
725
+ console.warn('Background private shuttle availability refresh failed; keeping cached availability visible.', err);
726
+ } else {
727
+ setError(err instanceof Error ? err.message : 'Failed to load availabilities');
728
+ }
647
729
  } finally {
648
730
  setLoadingAvailabilities(false);
649
731
  setIsFetchingMoreAvailabilities(false);
@@ -702,6 +784,100 @@ export function PrivateShuttleBookingFlow({
702
784
 
703
785
  const dates = useMemo(() => Object.keys(availabilitiesByDate).sort(), [availabilitiesByDate]);
704
786
 
787
+ useEffect(() => {
788
+ if (!selectedDate || !product.productId) return;
789
+ const selectedDateAvailabilities = availabilitiesByDate[selectedDate] ?? [];
790
+ if (!selectedDateAvailabilities.some((availability) => availability.isSummary)) return;
791
+
792
+ let cancelled = false;
793
+ async function hydrateSelectedDateDetails() {
794
+ setIsFetchingMoreAvailabilities(true);
795
+ try {
796
+ const result = await getAvailabilities(product.productId, selectedDate, selectedDate, {
797
+ allOptions: true,
798
+ promoCode: activePromoCode || undefined,
799
+ ...(pricingProfileIdForAvailabilities
800
+ ? { pricingProfileId: pricingProfileIdForAvailabilities }
801
+ : {}),
802
+ ...(cancellationPolicyProfileIdForAvailabilities
803
+ ? { cancellationPolicyProfileId: cancellationPolicyProfileIdForAvailabilities }
804
+ : {}),
805
+ });
806
+ if (cancelled) return;
807
+
808
+ if (result.pricingConfig && !pricingConfigSetRef.current) {
809
+ setPricingConfig(result.pricingConfig);
810
+ pricingConfigSetRef.current = true;
811
+ }
812
+ if (result.precomputedPrices) setPrecomputedPrices(result.precomputedPrices);
813
+ if (result.resourcePriceByCurrency) setResourcePriceByCurrency(result.resourcePriceByCurrency);
814
+ if (result.resourcePriceByOption) setResourcePriceByOption(result.resourcePriceByOption);
815
+
816
+ setAvailabilities((prev) => {
817
+ const merged = new Map(
818
+ prev.map((availability) => [
819
+ `${availability.dateTime}-${availability.productId || availability.productOptionId}`,
820
+ availability,
821
+ ])
822
+ );
823
+ result.availabilities.forEach((availability) => {
824
+ merged.set(`${availability.dateTime}-${availability.productId || availability.productOptionId}`, availability);
825
+ });
826
+ return Array.from(merged.values());
827
+ });
828
+
829
+ const cacheKey = availabilitiesCache
830
+ ? buildAvailabilitiesCacheKey(
831
+ product.productId,
832
+ activeOptionIdsKey,
833
+ activePromoCode,
834
+ pricingProfileIdForAvailabilities,
835
+ )
836
+ : null;
837
+ if (cacheKey && availabilitiesCache) {
838
+ const existingCache = availabilitiesCache.get(cacheKey);
839
+ const mergedAvailabilities = new Map(
840
+ (existingCache?.availabilities ?? []).map((availability) => [
841
+ `${availability.dateTime}-${availability.productId || availability.productOptionId}`,
842
+ availability,
843
+ ])
844
+ );
845
+ result.availabilities.forEach((availability) => {
846
+ mergedAvailabilities.set(`${availability.dateTime}-${availability.productId || availability.productOptionId}`, availability);
847
+ });
848
+ availabilitiesCache.merge(cacheKey, {
849
+ fetchedRanges: existingCache?.fetchedRanges ?? fetchedRangesRef.current,
850
+ availabilities: Array.from(mergedAvailabilities.values()),
851
+ pricingConfig: result.pricingConfig ?? existingCache?.pricingConfig ?? null,
852
+ precomputedPrices: result.precomputedPrices ?? existingCache?.precomputedPrices ?? null,
853
+ resourcePriceByCurrency: result.resourcePriceByCurrency ?? existingCache?.resourcePriceByCurrency ?? null,
854
+ resourcePriceByOption: result.resourcePriceByOption ?? existingCache?.resourcePriceByOption ?? null,
855
+ });
856
+ }
857
+ } catch (err) {
858
+ if (!cancelled) {
859
+ console.error('Error hydrating private shuttle availability details:', err);
860
+ }
861
+ } finally {
862
+ if (!cancelled) setIsFetchingMoreAvailabilities(false);
863
+ }
864
+ }
865
+
866
+ hydrateSelectedDateDetails();
867
+ return () => {
868
+ cancelled = true;
869
+ };
870
+ }, [
871
+ selectedDate,
872
+ availabilitiesByDate,
873
+ product.productId,
874
+ activeOptionIdsKey,
875
+ activePromoCode,
876
+ pricingProfileIdForAvailabilities,
877
+ cancellationPolicyProfileIdForAvailabilities,
878
+ availabilitiesCache,
879
+ ]);
880
+
705
881
  const earliestAvailabilityDate = useMemo(() => {
706
882
  if (dates.length === 0) return EARLIEST_AVAILABILITY_DATE;
707
883
  // Build date in company timezone at noon to avoid cross-timezone day shifts.
@@ -1319,6 +1495,32 @@ export function PrivateShuttleBookingFlow({
1319
1495
  const [hours, minutes] = selectedStartTime.split(':').map(Number);
1320
1496
  const startTimeISO = `${selectedDate}T${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:00-06:00`;
1321
1497
 
1498
+ try {
1499
+ const refreshed = await refreshSelectedDateDetailsForCheckout();
1500
+ const slot = findMergedPrivateShuttleAvailability(
1501
+ refreshed,
1502
+ selectedAvailability,
1503
+ selectedOption || null,
1504
+ selectedDate || null,
1505
+ companyTimezone
1506
+ );
1507
+ const passengersToReserve = isSpecialRequest
1508
+ ? Math.min(passengerCount, maxVacancies)
1509
+ : passengerCount;
1510
+ if (slot?.vacancies != null && (slot.vacancies === 0 || slot.vacancies < passengersToReserve)) {
1511
+ setError(
1512
+ describePrivateShuttleCapacityConflictMessage({
1513
+ passengersRequested: passengerCount,
1514
+ vacancies: slot.vacancies,
1515
+ })
1516
+ );
1517
+ setLoading(false);
1518
+ return;
1519
+ }
1520
+ } catch (refreshErr) {
1521
+ console.warn('Fresh private shuttle availability check failed before reserve; falling back to reserve validation.', refreshErr);
1522
+ }
1523
+
1322
1524
  const specialRequestNote = isSpecialRequest
1323
1525
  ? `Special request: Customer requested ${passengerCount} passengers (${resourceCount} shuttles needed). We reserved ${billableResourceCount} at standard capacity. Team to add ${resourceCount - billableResourceCount} more shuttle(s) and verify fleet availability.`
1324
1526
  : '';
@@ -1,7 +1,13 @@
1
- import type { RefObject } from 'react';
2
- import type { Product } from '../../lib/booking-api';
1
+ import type { ReactNode, RefObject } from 'react';
2
+ import type {
3
+ Availability,
4
+ CheckoutDependentAddOnSelection,
5
+ ItineraryDisplayStep,
6
+ Product,
7
+ } from '../../lib/booking-api';
3
8
  import type { BookingSourceMetadata } from '../../lib/booking/source-metadata';
4
9
  import type { Currency } from './CurrencySwitcher';
10
+ import type { PriceSummaryLine } from './PriceSummary';
5
11
  import type { BookingFlowUiOptions } from './booking-flow-ui';
6
12
  import type { ProviderDashboardChangeBookingPayload } from './provider-dashboard-change-booking';
7
13
 
@@ -96,6 +102,25 @@ export interface BookingFlowBaseProps {
96
102
  hideItineraryBox?: boolean;
97
103
  /** Partner / embed-only tweaks; omit for default website behavior. */
98
104
  flowUi?: BookingFlowUiOptions;
105
+ /**
106
+ * Optional inline extension rendered immediately below the Build Itinerary box once
107
+ * a date and shuttle time are selected. Used by photo-first dependent add-on flows.
108
+ */
109
+ afterItinerary?: ReactNode | ((context: BookingFlowAfterItineraryContext) => ReactNode);
110
+ /**
111
+ * Optional hook to insert add-on rows into the Build Itinerary box. Receives the
112
+ * base itinerary from the selected shuttle and returns the display list to render.
113
+ */
114
+ augmentItineraryItems?: (context: BookingFlowAfterItineraryContext) => ItineraryDisplayStep[] | null;
115
+ /** Optional hook for embed flows to show extra rows in the checkout receipt summary. */
116
+ augmentPriceSummary?: (context: BookingFlowPriceSummaryContext) => BookingFlowPriceSummaryAugmentation | null;
117
+ /**
118
+ * Optional dependent add-on to create with the primary booking after checkout.
119
+ * Used by photo-first flows where the add-on is selected before the shuttle booking exists.
120
+ */
121
+ dependentAddOnSelection?: CheckoutDependentAddOnSelection | null;
122
+ /** Optional external validation before checkout submit (e.g. required add-on questionnaire answers). */
123
+ validateBeforeCheckout?: (context: BookingFlowAfterItineraryContext) => string | null;
99
124
  /** Explicit reserve/checkout source metadata (browser URL layer + optional portal merge at call site). */
100
125
  bookingSourceAttribution: Partial<BookingSourceMetadata>;
101
126
  /** Dedicated partner portal app (`booking.*`): persist reserve `source` as PARTNER_PORTAL. */
@@ -108,6 +133,29 @@ export interface BookingFlowBaseProps {
108
133
  changeProductOptions?: Product[];
109
134
  }
110
135
 
136
+ export interface BookingFlowAfterItineraryContext {
137
+ selectedDate: string | null;
138
+ selectedAvailability: Availability | null;
139
+ itineraryItems: ItineraryDisplayStep[] | null;
140
+ }
141
+
142
+ export interface BookingFlowPriceSummaryContext {
143
+ selectedDate: string | null;
144
+ selectedAvailability: Availability | null;
145
+ itineraryItems?: ItineraryDisplayStep[] | null;
146
+ priceSummaryLines: PriceSummaryLine[];
147
+ subtotal: number;
148
+ tax: number;
149
+ total: number;
150
+ currency: Currency;
151
+ }
152
+
153
+ export interface BookingFlowPriceSummaryAugmentation {
154
+ lines?: PriceSummaryLine[];
155
+ subtotalAdjustment?: number;
156
+ totalAdjustment?: number;
157
+ }
158
+
111
159
  /** Standard (new) reservation flow — no change-booking receipt or callbacks. */
112
160
  export interface NewBookingFlowProps extends BookingFlowBaseProps {}
113
161
 
@@ -45,6 +45,8 @@ export type ProviderDashboardChangePricingUi = {
45
45
  export interface BookingFlowUiOptions {
46
46
  showCollage?: boolean;
47
47
  showTourDescription?: boolean;
48
+ /** When false, the flow still uses the selected/initial date but does not render the calendar picker. */
49
+ showCalendar?: boolean;
48
50
  autoSelectFirstAvailableDate?: boolean;
49
51
  autoSelectFirstHighlightedPickup?: boolean;
50
52
  /**
@@ -9,8 +9,8 @@ import {
9
9
  } from 'react';
10
10
  import type { Availability, PricingConfig, PrecomputedPricesByCategory } from '../lib/booking-api';
11
11
 
12
- /** Cache TTL in milliseconds (5 minutes). Entries older than this are considered stale. */
13
- export const AVAILABILITIES_CACHE_TTL_MS = 5 * 60 * 1000;
12
+ /** Cache TTL in milliseconds (1 minute). Entries older than this are considered stale. */
13
+ export const AVAILABILITIES_CACHE_TTL_MS = 60 * 1000;
14
14
 
15
15
  export interface CachedAvailabilitiesData {
16
16
  fetchedRanges: Array<{ start: Date; end: Date }>;
@@ -355,6 +355,7 @@ export interface OrderSummaryFeeLine {
355
355
  name: string;
356
356
  totalAmount: number;
357
357
  description?: string;
358
+ showQuantityLabel?: boolean;
358
359
  }
359
360
 
360
361
  /** One ticket line for order summary (category, qty, price per unit, item total in display currency). */
@@ -1298,6 +1298,7 @@ export interface Availability {
1298
1298
  pricesByCategory?: {
1299
1299
  retailPrices: Array<{ category: string; price: number }>;
1300
1300
  };
1301
+ isSummary?: boolean;
1301
1302
  }
1302
1303
 
1303
1304
  export type PrecomputedPricesByCategory = Record<string, Record<string, number>>;
@@ -1306,13 +1307,16 @@ export interface GetAvailabilitiesResponse {
1306
1307
  availabilities: Availability[];
1307
1308
  pricingConfig?: PricingConfig;
1308
1309
  precomputedPrices?: PrecomputedPricesByCategory;
1310
+ precomputedPricesByOption?: Record<string, PrecomputedPricesByCategory>;
1309
1311
  resourcePriceByCurrency?: Record<string, number>;
1310
1312
  resourcePriceByOption?: Record<string, Record<string, number>>;
1313
+ responseMode?: 'summary' | string;
1311
1314
  }
1312
1315
 
1313
1316
  export interface GetAvailabilitiesOptions {
1314
1317
  promoCode?: string | null;
1315
1318
  allOptions?: boolean;
1319
+ summary?: boolean;
1316
1320
  /**
1317
1321
  * When set, TicketBooth should return rates/precomputed prices for this partner pricing profile.
1318
1322
  * Must match the profile linked on the partner (e.g. capabilities.pricingProfileId).
@@ -1359,6 +1363,7 @@ export async function getAvailabilities(
1359
1363
  });
1360
1364
  if (options?.promoCode?.trim()) params.set('promoCode', options.promoCode.trim());
1361
1365
  if (options?.allOptions === true) params.set('allOptions', 'true');
1366
+ if (options?.summary === true) params.set('summary', 'true');
1362
1367
  const pricingProfileId = options?.pricingProfileId?.trim();
1363
1368
  if (pricingProfileId) params.set('pricingProfileId', pricingProfileId);
1364
1369
  const cancellationPolicyProfileId = options?.cancellationPolicyProfileId?.trim();
@@ -1407,12 +1412,17 @@ export async function getAvailabilities(
1407
1412
  }
1408
1413
 
1409
1414
  const bookingData = (data as { data?: GetAvailabilitiesResponse } | null)?.data;
1415
+ const isSummary = options?.summary === true || bookingData?.responseMode === 'summary';
1410
1416
  return {
1411
- availabilities: bookingData?.availabilities ?? [],
1417
+ availabilities: (bookingData?.availabilities ?? []).map((availability) =>
1418
+ isSummary ? { ...availability, isSummary: true } : availability
1419
+ ),
1412
1420
  pricingConfig: bookingData?.pricingConfig,
1413
1421
  precomputedPrices: bookingData?.precomputedPrices,
1422
+ precomputedPricesByOption: bookingData?.precomputedPricesByOption,
1414
1423
  resourcePriceByCurrency: bookingData?.resourcePriceByCurrency,
1415
1424
  resourcePriceByOption: bookingData?.resourcePriceByOption,
1425
+ responseMode: bookingData?.responseMode,
1416
1426
  };
1417
1427
  }
1418
1428
 
@@ -1649,6 +1659,16 @@ export interface CheckoutBreakdown {
1649
1659
  currency: string;
1650
1660
  }
1651
1661
 
1662
+ export interface CheckoutDependentAddOnSelection {
1663
+ dependentAddOnProductId: string;
1664
+ dependentAddOnProductOptionId?: string;
1665
+ offeringId: string;
1666
+ slotStart: string;
1667
+ slotEnd: string;
1668
+ quantity: number;
1669
+ checkoutAnswers?: Record<string, string>;
1670
+ }
1671
+
1652
1672
  export interface CreatePaymentIntentRequest {
1653
1673
  productId: string;
1654
1674
  optionId: string;
@@ -1667,6 +1687,7 @@ export interface CreatePaymentIntentRequest {
1667
1687
  cancellationPolicyId?: string;
1668
1688
  termsAcceptedAt?: string;
1669
1689
  checkoutBreakdown?: CheckoutBreakdown;
1690
+ dependentAddOnSelection?: CheckoutDependentAddOnSelection;
1670
1691
  itineraryDisplay?: ItineraryDisplayStep[];
1671
1692
  skipConfirmationCommunications?: boolean;
1672
1693
  disableAutoCommunications?: boolean;
@@ -1765,6 +1786,7 @@ export interface ConfirmFreeBookingRequest {
1765
1786
  pickupLocationId?: string;
1766
1787
  termsAcceptedAt?: string;
1767
1788
  itineraryDisplay?: ItineraryDisplayStep[];
1789
+ dependentAddOnSelection?: CheckoutDependentAddOnSelection;
1768
1790
  skipConfirmationCommunications?: boolean;
1769
1791
  disableAutoCommunications?: boolean;
1770
1792
  source?: string;
@@ -1846,6 +1868,7 @@ export interface ConfirmBookingWithoutPaymentRequest {
1846
1868
  skipConfirmationCommunications?: boolean;
1847
1869
  disableAutoCommunications?: boolean;
1848
1870
  checkoutBreakdown: CheckoutBreakdown;
1871
+ dependentAddOnSelection?: CheckoutDependentAddOnSelection;
1849
1872
  depositAmount: number;
1850
1873
  balanceAmount: number;
1851
1874
  totalAmount: number;
@@ -74,6 +74,8 @@ export interface BookingRuntimeCatalog {
74
74
  getProductDescription: (...args: any[]) => any;
75
75
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
76
  buildMinimalProductFromConfig?: (...args: any[]) => any;
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ getStaticProductByIdOrSlug?: (...args: any[]) => any;
77
79
  /** Image URL helper from the host catalog (e.g. Via Via `constants/images`). */
78
80
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
81
  getImageUrl?: (...args: any[]) => any;