@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.
@@ -130,7 +130,7 @@ function findMergedPrivateShuttleAvailability(
130
130
  tz: string
131
131
  ): Availability | undefined {
132
132
  if (!sel || !optionId || !selectedDateStr) return undefined;
133
- const oidMatches = (a: Availability) => (a.productOptionId ?? a.productId) === optionId;
133
+ const oidMatches = (a: Availability) => getPrivateShuttleAvailabilityOptionId(a) === optionId;
134
134
  const availId = sel.availabilityId?.trim();
135
135
  if (availId) {
136
136
  const hit = merged.find((a) => a.availabilityId === availId && oidMatches(a));
@@ -146,6 +146,40 @@ function findMergedPrivateShuttleAvailability(
146
146
  return onDay[0];
147
147
  }
148
148
 
149
+ function getPrivateShuttleAvailabilityOptionId(availability: Availability): string {
150
+ const productOptionId = availability.productOptionId?.trim();
151
+ if (productOptionId) return productOptionId;
152
+ const productId = availability.productId?.trim();
153
+ return productId?.startsWith('po_') ? productId : '';
154
+ }
155
+
156
+ function getPrivateShuttleAvailabilityCacheKey(availability: Availability): string {
157
+ return `${availability.dateTime}-${getPrivateShuttleAvailabilityOptionId(availability)}`;
158
+ }
159
+
160
+ function getResourceRate(availability: Availability | undefined | null) {
161
+ return availability?.rates?.find((r) => r.rateId === 'RESOURCE' || r.category === 'RESOURCE');
162
+ }
163
+
164
+ function applyResourceAdjustments(
165
+ basePrice: number,
166
+ availability: Availability | undefined | null,
167
+ currency: Currency
168
+ ): number {
169
+ const resourceRate = getResourceRate(availability);
170
+ const adjustmentTotal =
171
+ resourceRate?.appliedAdjustments?.reduce(
172
+ (sum, adjustment) => sum + (adjustment.changeByCurrency?.[currency] ?? 0),
173
+ 0
174
+ ) ??
175
+ resourceRate?.applied_adjustments?.reduce(
176
+ (sum, adjustment) => sum + (adjustment.changeByCurrency?.[currency] ?? 0),
177
+ 0
178
+ ) ??
179
+ 0;
180
+ return basePrice + adjustmentTotal;
181
+ }
182
+
149
183
  export function PrivateShuttleBookingFlow({
150
184
  product,
151
185
  productId,
@@ -394,11 +428,9 @@ export function PrivateShuttleBookingFlow({
394
428
 
395
429
  let mergedAvailabilities: Availability[] = [];
396
430
  setAvailabilities((prev) => {
397
- const existingMap = new Map(
398
- prev.map((avail) => [`${avail.dateTime}-${avail.productId || avail.productOptionId}`, avail])
399
- );
431
+ const existingMap = new Map(prev.map((avail) => [getPrivateShuttleAvailabilityCacheKey(avail), avail]));
400
432
  result.availabilities.forEach((avail) => {
401
- existingMap.set(`${avail.dateTime}-${avail.productId || avail.productOptionId}`, avail);
433
+ existingMap.set(getPrivateShuttleAvailabilityCacheKey(avail), avail);
402
434
  });
403
435
  mergedAvailabilities = Array.from(existingMap.values());
404
436
  return mergedAvailabilities;
@@ -476,11 +508,9 @@ export function PrivateShuttleBookingFlow({
476
508
 
477
509
  let mergedAvailabilities: Availability[] = [];
478
510
  setAvailabilities((prev) => {
479
- const existingMap = new Map(
480
- prev.map((avail) => [`${avail.dateTime}-${avail.productId || avail.productOptionId}`, avail])
481
- );
511
+ const existingMap = new Map(prev.map((avail) => [getPrivateShuttleAvailabilityCacheKey(avail), avail]));
482
512
  result.availabilities.forEach((avail) => {
483
- existingMap.set(`${avail.dateTime}-${avail.productId || avail.productOptionId}`, avail);
513
+ existingMap.set(getPrivateShuttleAvailabilityCacheKey(avail), avail);
484
514
  });
485
515
  mergedAvailabilities = Array.from(existingMap.values());
486
516
  return mergedAvailabilities;
@@ -498,12 +528,12 @@ export function PrivateShuttleBookingFlow({
498
528
  const existingCache = availabilitiesCache.get(cacheKey);
499
529
  const mergedAvailabilitiesMap = new Map(
500
530
  (existingCache?.availabilities ?? []).map((availability) => [
501
- `${availability.dateTime}-${availability.productId || availability.productOptionId}`,
531
+ getPrivateShuttleAvailabilityCacheKey(availability),
502
532
  availability,
503
533
  ])
504
534
  );
505
535
  result.availabilities.forEach((availability) => {
506
- mergedAvailabilitiesMap.set(`${availability.dateTime}-${availability.productId || availability.productOptionId}`, availability);
536
+ mergedAvailabilitiesMap.set(getPrivateShuttleAvailabilityCacheKey(availability), availability);
507
537
  });
508
538
  availabilitiesCache.merge(cacheKey, {
509
539
  fetchedRanges: existingCache?.fetchedRanges ?? fetchedRangesRef.current,
@@ -687,13 +717,12 @@ export function PrivateShuttleBookingFlow({
687
717
  if (existing.length === 0) return result.availabilities;
688
718
  const existingMap = new Map(
689
719
  existing.map((avail) => [
690
- `${avail.dateTime}-${avail.productId || avail.productOptionId}`,
720
+ getPrivateShuttleAvailabilityCacheKey(avail),
691
721
  avail,
692
722
  ])
693
723
  );
694
724
  result.availabilities.forEach((avail) => {
695
- const key = `${avail.dateTime}-${avail.productId || avail.productOptionId}`;
696
- existingMap.set(key, avail);
725
+ existingMap.set(getPrivateShuttleAvailabilityCacheKey(avail), avail);
697
726
  });
698
727
  return Array.from(existingMap.values());
699
728
  })();
@@ -779,9 +808,7 @@ export function PrivateShuttleBookingFlow({
779
808
  const optionsAvailableForSelectedDate = useMemo(() => {
780
809
  if (!selectedDate) return [];
781
810
  const dateAvailabilities = availabilitiesByDate[selectedDate] || [];
782
- const optionIds = new Set(
783
- dateAvailabilities.map((a) => a.productId || a.productOptionId).filter(Boolean)
784
- );
811
+ const optionIds = new Set(dateAvailabilities.map(getPrivateShuttleAvailabilityOptionId).filter(Boolean));
785
812
  return activeOptions.filter((opt) => optionIds.has(opt.optionId));
786
813
  }, [selectedDate, availabilitiesByDate, activeOptions]);
787
814
 
@@ -819,12 +846,12 @@ export function PrivateShuttleBookingFlow({
819
846
  setAvailabilities((prev) => {
820
847
  const merged = new Map(
821
848
  prev.map((availability) => [
822
- `${availability.dateTime}-${availability.productId || availability.productOptionId}`,
849
+ getPrivateShuttleAvailabilityCacheKey(availability),
823
850
  availability,
824
851
  ])
825
852
  );
826
853
  result.availabilities.forEach((availability) => {
827
- merged.set(`${availability.dateTime}-${availability.productId || availability.productOptionId}`, availability);
854
+ merged.set(getPrivateShuttleAvailabilityCacheKey(availability), availability);
828
855
  });
829
856
  return Array.from(merged.values());
830
857
  });
@@ -841,12 +868,12 @@ export function PrivateShuttleBookingFlow({
841
868
  const existingCache = availabilitiesCache.get(cacheKey);
842
869
  const mergedAvailabilities = new Map(
843
870
  (existingCache?.availabilities ?? []).map((availability) => [
844
- `${availability.dateTime}-${availability.productId || availability.productOptionId}`,
871
+ getPrivateShuttleAvailabilityCacheKey(availability),
845
872
  availability,
846
873
  ])
847
874
  );
848
875
  result.availabilities.forEach((availability) => {
849
- mergedAvailabilities.set(`${availability.dateTime}-${availability.productId || availability.productOptionId}`, availability);
876
+ mergedAvailabilities.set(getPrivateShuttleAvailabilityCacheKey(availability), availability);
850
877
  });
851
878
  availabilitiesCache.merge(cacheKey, {
852
879
  fetchedRanges: existingCache?.fetchedRanges ?? fetchedRangesRef.current,
@@ -898,22 +925,23 @@ export function PrivateShuttleBookingFlow({
898
925
 
899
926
  const getOptionPrice = useCallback(
900
927
  (optionId: string) => {
928
+ const opt = activeOptions.find((o) => o.optionId === optionId);
901
929
  const dateAvailabilities = selectedDate ? availabilitiesByDate[selectedDate] || [] : [];
902
930
  const avail = dateAvailabilities.find(
903
- (a) => (a.productId || a.productOptionId) === optionId
931
+ (a) => getPrivateShuttleAvailabilityOptionId(a) === optionId
904
932
  );
933
+ if (currency === 'CAD' && opt?.pricing?.['RESOURCE'] != null) {
934
+ return applyResourceAdjustments(opt.pricing['RESOURCE'], avail, currency);
935
+ }
905
936
  if (avail?.rates) {
906
- const resourceRate = avail.rates.find(
907
- (r) => r.rateId === 'RESOURCE' || r.category === 'RESOURCE'
908
- );
937
+ const resourceRate = getResourceRate(avail);
909
938
  if (resourceRate?.priceByCurrency?.[currency] != null)
910
939
  return resourceRate.priceByCurrency[currency];
911
940
  if (currency === 'CAD' && resourceRate?.price != null) return resourceRate.price;
912
941
  }
913
942
  if (resourcePriceByOption?.[optionId]?.[currency] != null)
914
943
  return resourcePriceByOption[optionId][currency];
915
- if (resourcePriceByCurrency?.[currency] != null) return resourcePriceByCurrency[currency];
916
- const opt = activeOptions.find((o) => o.optionId === optionId);
944
+ if (currency === 'CAD' && resourcePriceByCurrency?.[currency] != null) return resourcePriceByCurrency[currency];
917
945
  return opt?.pricing?.['RESOURCE'] ?? 0;
918
946
  },
919
947
  [
@@ -927,10 +955,14 @@ export function PrivateShuttleBookingFlow({
927
955
  );
928
956
 
929
957
  const resourcePrice = useMemo(() => {
930
- if (selectedAvailability?.rates) {
931
- const resourceRate = selectedAvailability.rates.find(
932
- (r) => r.rateId === 'RESOURCE' || r.category === 'RESOURCE'
958
+ if (currency === 'CAD' && selectedOptionConfig?.pricing?.['RESOURCE'] != null)
959
+ return applyResourceAdjustments(
960
+ selectedOptionConfig.pricing['RESOURCE'],
961
+ selectedAvailability,
962
+ currency
933
963
  );
964
+ if (selectedAvailability?.rates) {
965
+ const resourceRate = getResourceRate(selectedAvailability);
934
966
  if (resourceRate) {
935
967
  const fromRate =
936
968
  resourceRate.priceByCurrency?.[currency] ??
@@ -940,10 +972,12 @@ export function PrivateShuttleBookingFlow({
940
972
  }
941
973
  if (selectedOption && resourcePriceByOption?.[selectedOption]?.[currency] != null)
942
974
  return resourcePriceByOption[selectedOption][currency];
943
- if (resourcePriceByCurrency?.[currency] != null) return resourcePriceByCurrency[currency];
975
+ if (currency === 'CAD' && resourcePriceByCurrency?.[currency] != null) return resourcePriceByCurrency[currency];
944
976
  if (precomputedPrices?.['RESOURCE']?.[currency] != null)
945
977
  return precomputedPrices['RESOURCE'][currency];
946
- return selectedOptionConfig?.pricing?.['RESOURCE'] ?? 0;
978
+ if (selectedOptionConfig?.pricing?.['RESOURCE'] != null)
979
+ return selectedOptionConfig.pricing['RESOURCE'];
980
+ return 0;
947
981
  }, [
948
982
  selectedAvailability,
949
983
  resourcePriceByOption,
@@ -1173,7 +1207,7 @@ export function PrivateShuttleBookingFlow({
1173
1207
  setSelectedOption(opt.optionId);
1174
1208
  const dateAvailabilities = availabilitiesByDate[selectedDate] || [];
1175
1209
  const avail = dateAvailabilities.find(
1176
- (a) => (a.productId || a.productOptionId) === opt.optionId
1210
+ (a) => getPrivateShuttleAvailabilityOptionId(a) === opt.optionId
1177
1211
  );
1178
1212
  setSelectedAvailability(avail || null);
1179
1213
  }
@@ -1231,7 +1265,7 @@ export function PrivateShuttleBookingFlow({
1231
1265
  if (selectedDate) {
1232
1266
  const dateAvailabilities = availabilitiesByDate[selectedDate] || [];
1233
1267
  const avail = dateAvailabilities.find(
1234
- (a) => (a.productId || a.productOptionId) === optionId
1268
+ (a) => getPrivateShuttleAvailabilityOptionId(a) === optionId
1235
1269
  );
1236
1270
  setSelectedAvailability(avail || null);
1237
1271
  } else {
@@ -61,10 +61,12 @@ export function buildAvailabilitiesCacheKey(
61
61
  productId: string,
62
62
  optionIdsKey: string,
63
63
  promoCode: string | null,
64
- pricingProfileId?: string | null
64
+ pricingProfileId?: string | null,
65
+ cancellationPolicyProfileId?: string | null,
65
66
  ): string {
66
67
  const pp = (pricingProfileId ?? '').trim();
67
- return `${productId}|${optionIdsKey}|${promoCode ?? ''}|pp:${pp}`;
68
+ const cp = (cancellationPolicyProfileId ?? '').trim();
69
+ return `${productId}|${optionIdsKey}|${promoCode ?? ''}|pp:${pp}|cp:${cp}`;
68
70
  }
69
71
 
70
72
  export function AvailabilitiesCacheProvider({ children }: { children: ReactNode }) {
@@ -331,10 +331,14 @@ export function computePriceBreakdown(
331
331
 
332
332
  // Subtotal in display currency = base + adjustments; pass so final price is (subtotal + fees) * (1+tax) and breakdown adds up
333
333
  const subtotalInDisplayCurrency = baseAmount + adjustmentSum;
334
+ const backendPriceForDetails =
335
+ currency === BASE_CURRENCY && subtotalInDisplayCurrency > 0
336
+ ? subtotalInDisplayCurrency
337
+ : backendPriceCAD;
334
338
  const details = computeTicketPriceDetails(
335
339
  pricingConfig,
336
340
  currency,
337
- backendPriceCAD,
341
+ backendPriceForDetails,
338
342
  hasFees,
339
343
  subtotalInDisplayCurrency > 0 ? subtotalInDisplayCurrency : baseInDisplayCurrency
340
344
  );
@@ -1623,6 +1623,7 @@ export interface GetAvailabilitiesOptions {
1623
1623
  promoCode?: string | null;
1624
1624
  allOptions?: boolean;
1625
1625
  summary?: boolean;
1626
+ responseMode?: 'summary' | 'booking-day';
1626
1627
  /**
1627
1628
  * When set, TicketBooth should return rates/precomputed prices for this partner pricing profile.
1628
1629
  * Must match the profile linked on the partner (e.g. capabilities.pricingProfileId).
@@ -1670,6 +1671,7 @@ export async function getAvailabilities(
1670
1671
  if (options?.promoCode?.trim()) params.set('promoCode', options.promoCode.trim());
1671
1672
  if (options?.allOptions === true) params.set('allOptions', 'true');
1672
1673
  if (options?.summary === true) params.set('summary', 'true');
1674
+ if (options?.responseMode) params.set('responseMode', options.responseMode);
1673
1675
  const pricingProfileId = options?.pricingProfileId?.trim();
1674
1676
  if (pricingProfileId) params.set('pricingProfileId', pricingProfileId);
1675
1677
  const cancellationPolicyProfileId = options?.cancellationPolicyProfileId?.trim();