@ticketboothapp/booking 1.2.125 → 1.2.127

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.125",
3
+ "version": "1.2.127",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -12,6 +12,8 @@ interface AdminPaymentChoiceModalProps {
12
12
  onPayNow: () => void;
13
13
  onConfirmWithoutPayment: () => void;
14
14
  onCancel: () => void;
15
+ description?: string;
16
+ payNowLabel?: string;
15
17
  }
16
18
 
17
19
  /**
@@ -28,6 +30,8 @@ export function AdminPaymentChoiceModal({
28
30
  onPayNow,
29
31
  onConfirmWithoutPayment,
30
32
  onCancel,
33
+ description,
34
+ payNowLabel,
31
35
  }: AdminPaymentChoiceModalProps) {
32
36
  if (!open) return null;
33
37
 
@@ -59,7 +63,7 @@ export function AdminPaymentChoiceModal({
59
63
  </button>
60
64
  </div>
61
65
  <p className="mt-3 text-sm text-stone-600 leading-relaxed">
62
- Pay now, or confirm without payment. The customer can pay the full balance from the Manage Booking page.
66
+ {description ?? 'Pay now, or confirm without payment. The customer can pay the full balance from the Manage Booking page.'}
63
67
  </p>
64
68
  </div>
65
69
 
@@ -76,7 +80,7 @@ export function AdminPaymentChoiceModal({
76
80
  disabled={loading}
77
81
  className="w-full py-3 px-4 bg-emerald-600 text-white font-semibold rounded-lg hover:bg-emerald-700 disabled:opacity-50 disabled:cursor-not-allowed"
78
82
  >
79
- {loading ? 'Loading...' : `Pay now (${formatCurrencyAmount(totalAmount, currency)})`}
83
+ {loading ? 'Loading...' : `${payNowLabel ?? 'Pay now'} (${formatCurrencyAmount(totalAmount, currency)})`}
80
84
  </button>
81
85
  <button
82
86
  type="button"
@@ -185,6 +185,7 @@ export function PrivateShuttleBookingFlow({
185
185
  activePromoCode,
186
186
  promoCodeError,
187
187
  promoCodeValidating,
188
+ forcedCancellationPolicy,
188
189
  handlePromoInputChange,
189
190
  handleApplyPromo,
190
191
  handleRemovePromo,
@@ -197,6 +198,7 @@ export function PrivateShuttleBookingFlow({
197
198
  resourceCount: Math.ceil(passengerCount / PRIVATE_SHUTTLE_RESOURCE_CAPACITY),
198
199
  productCompanyId: product.companyId,
199
200
  productId: product.productId,
201
+ setCancellationPolicyId,
200
202
  onPromoChangesAvailability: () => clearFetchedAvailabilityRangesRef.current(),
201
203
  t,
202
204
  });
@@ -943,6 +945,7 @@ export function PrivateShuttleBookingFlow({
943
945
  }}
944
946
  cancellationPolicies={pricingConfig?.cancellationPolicies}
945
947
  cancellationPolicyId={cancellationPolicyId}
948
+ forcedCancellationPolicy={forcedCancellationPolicy}
946
949
  onCancellationPolicySelect={setCancellationPolicyId}
947
950
  skipConfirmationCommunications={skipConfirmationCommunications}
948
951
  disableAutoCommunications={disableAutoCommunications}
@@ -57,6 +57,12 @@ export function PrivateShuttleCheckoutDialogs({
57
57
  currency={currency}
58
58
  loading={loading}
59
59
  error={error}
60
+ description={
61
+ adminChoiceData?.isDepositPayment
62
+ ? 'Pay the deposit now, or confirm without payment. The customer can pay the deposit or remaining balance from the Manage Booking page.'
63
+ : undefined
64
+ }
65
+ payNowLabel={adminChoiceData?.isDepositPayment ? 'Pay deposit' : undefined}
60
66
  onPayNow={onPayNow}
61
67
  onConfirmWithoutPayment={onConfirmWithoutPayment}
62
68
  onCancel={onAdminPaymentChoiceCancel}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { formatCurrencyAmount } from '../../lib/currency';
4
- import type { PricingConfig } from '../../lib/booking-api';
4
+ import type { PricingConfig, RefundTierOption } from '../../lib/booking-api';
5
5
  import type { Currency } from './CurrencySwitcher';
6
6
  import type { Locale } from '../../lib/booking/i18n/config';
7
7
  import { CancellationPolicySelector } from './CancellationPolicySelector';
@@ -46,6 +46,12 @@ interface PrivateShuttleCheckoutSectionProps {
46
46
  onPhoneNumberChange: (value: string) => void;
47
47
  cancellationPolicies?: PricingConfig['cancellationPolicies'];
48
48
  cancellationPolicyId: string | null;
49
+ forcedCancellationPolicy?: {
50
+ id: string;
51
+ label: string;
52
+ refundTiers?: RefundTierOption[];
53
+ changeWindowHoursBefore?: number | null;
54
+ } | null;
49
55
  onCancellationPolicySelect: (policyId: string) => void;
50
56
  skipConfirmationCommunications: boolean;
51
57
  disableAutoCommunications: boolean;
@@ -97,6 +103,7 @@ export function PrivateShuttleCheckoutSection({
97
103
  onPhoneNumberChange,
98
104
  cancellationPolicies,
99
105
  cancellationPolicyId,
106
+ forcedCancellationPolicy,
100
107
  onCancellationPolicySelect,
101
108
  skipConfirmationCommunications,
102
109
  disableAutoCommunications,
@@ -117,7 +124,7 @@ export function PrivateShuttleCheckoutSection({
117
124
  }: PrivateShuttleCheckoutSectionProps) {
118
125
  const amountDueToday = depositInfo ? depositInfo.depositAmount : totalPrice;
119
126
  const formattedAmountDueToday = formatCurrencyAmount(amountDueToday, currency, locale);
120
- const hasCancellationPolicies = Boolean(cancellationPolicies?.length);
127
+ const hasCancellationPolicies = Boolean(cancellationPolicies?.length || forcedCancellationPolicy);
121
128
 
122
129
  return (
123
130
  <>
@@ -241,6 +248,7 @@ export function PrivateShuttleCheckoutSection({
241
248
  locale={locale}
242
249
  t={t}
243
250
  onPolicySelect={onCancellationPolicySelect}
251
+ forcedPolicy={forcedCancellationPolicy}
244
252
  rowSubtitle={
245
253
  <>
246
254
  <b>Your deposit today is 100% refundable</b> - only once we align on an itinerary
@@ -487,7 +487,12 @@ export function usePrivateShuttleCheckoutController({
487
487
  setAdminChoiceData(buildPrivateShuttleAdminChoiceData({
488
488
  reservation,
489
489
  checkoutBreakdown,
490
- totalAmount: checkoutDepositInfo.totalPrice,
490
+ totalAmount: checkoutDepositInfo.depositAmount,
491
+ fullTotalAmount: checkoutDepositInfo.totalPrice,
492
+ depositAmount: checkoutDepositInfo.depositAmount,
493
+ balanceAmount: checkoutDepositInfo.balanceAmount,
494
+ balanceChargeDaysBefore: balanceChargeDaysBefore ?? 7,
495
+ isDepositPayment: true,
491
496
  datePart: selectedDate,
492
497
  timePart: selectedStartTime,
493
498
  availabilityProductOptionId: selectedOption,
@@ -42,6 +42,11 @@ export interface PrivateShuttleAdminChoiceData {
42
42
  reservationExpiration?: string;
43
43
  checkoutBreakdown: CheckoutBreakdown;
44
44
  totalAmount: number;
45
+ fullTotalAmount?: number;
46
+ depositAmount?: number;
47
+ balanceAmount?: number;
48
+ balanceChargeDaysBefore?: number;
49
+ isDepositPayment?: boolean;
45
50
  datePart: string;
46
51
  timePart: string;
47
52
  availabilityProductOptionId: string;
@@ -67,6 +72,11 @@ export interface BuildPrivateShuttleAdminChoiceDataParams {
67
72
  reservation: { reservationReference: string; expiresAt?: string };
68
73
  checkoutBreakdown: CheckoutBreakdown;
69
74
  totalAmount: number;
75
+ fullTotalAmount?: number;
76
+ depositAmount?: number;
77
+ balanceAmount?: number;
78
+ balanceChargeDaysBefore?: number;
79
+ isDepositPayment?: boolean;
70
80
  datePart: string;
71
81
  timePart: string;
72
82
  availabilityProductOptionId: string;
@@ -176,7 +186,7 @@ export function buildPrivateShuttlePayNowCheckoutModalData(
176
186
  ticketLines: adminChoiceData.ticketLinesForModal,
177
187
  feeLineItems: adminChoiceData.feeLineItems,
178
188
  returnPriceAdjustment: 0,
179
- subtotal: adminChoiceData.subtotal,
189
+ subtotal: adminChoiceData.fullTotalAmount ?? adminChoiceData.subtotal,
180
190
  tax: adminChoiceData.tax,
181
191
  total: adminChoiceData.totalAmount,
182
192
  totalQuantity: adminChoiceData.totalQuantity,
@@ -186,7 +196,17 @@ export function buildPrivateShuttlePayNowCheckoutModalData(
186
196
  cancellationPolicyLabel: adminChoiceData.cancellationPolicyLabel,
187
197
  promoDiscountAmount: adminChoiceData.promoDiscountAmount,
188
198
  discountLabel: adminChoiceData.discountLabel,
189
- summaryOverride: adminChoiceData.summaryOverride,
199
+ summaryOverride: adminChoiceData.summaryOverride
200
+ ? {
201
+ ...adminChoiceData.summaryOverride,
202
+ fullTotalAmount:
203
+ adminChoiceData.summaryOverride.fullTotalAmount ??
204
+ adminChoiceData.fullTotalAmount ??
205
+ adminChoiceData.totalAmount,
206
+ }
207
+ : undefined,
208
+ isDepositPayment: adminChoiceData.isDepositPayment,
209
+ balanceChargeDaysBefore: adminChoiceData.balanceChargeDaysBefore,
190
210
  };
191
211
  }
192
212
 
@@ -255,9 +275,10 @@ export async function confirmPrivateShuttleAdminBookingWithoutPayment({
255
275
  skipConfirmationCommunications: skipConfirmationCommunications ? true : undefined,
256
276
  disableAutoCommunications: disableAutoCommunications ? true : undefined,
257
277
  checkoutBreakdown: adminChoiceData.checkoutBreakdown,
258
- depositAmount: 0,
259
- balanceAmount: adminChoiceData.totalAmount,
260
- totalAmount: adminChoiceData.totalAmount,
278
+ depositAmount: adminChoiceData.depositAmount ?? 0,
279
+ balanceAmount: adminChoiceData.balanceAmount ?? adminChoiceData.totalAmount,
280
+ totalAmount: adminChoiceData.fullTotalAmount ?? adminChoiceData.totalAmount,
281
+ balanceChargeDaysBefore: adminChoiceData.balanceChargeDaysBefore,
261
282
  pricingQuoteId: adminChoiceData.pricingQuoteId,
262
283
  pricingQuoteInputsHash: adminChoiceData.pricingQuoteInputsHash,
263
284
  ...bookingSourceContext,
@@ -2,10 +2,18 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import {
3
3
  validatePromoCode,
4
4
  type Availability,
5
+ type RefundTierOption,
5
6
  } from '../../lib/booking-api';
6
7
 
7
8
  type TranslationFn = (key: string) => string;
8
9
 
10
+ export type PrivateShuttleForcedCancellationPolicy = {
11
+ id: string;
12
+ label: string;
13
+ refundTiers?: RefundTierOption[];
14
+ changeWindowHoursBefore?: number | null;
15
+ } | null;
16
+
9
17
  export interface UsePrivateShuttlePromoControllerParams {
10
18
  initialPromoCode?: string | null;
11
19
  isAdmin: boolean;
@@ -15,6 +23,7 @@ export interface UsePrivateShuttlePromoControllerParams {
15
23
  resourceCount: number;
16
24
  productCompanyId?: string | null;
17
25
  productId: string;
26
+ setCancellationPolicyId: (policyId: string | null) => void;
18
27
  onPromoChangesAvailability: () => void;
19
28
  t: TranslationFn;
20
29
  }
@@ -28,6 +37,7 @@ export function usePrivateShuttlePromoController({
28
37
  resourceCount,
29
38
  productCompanyId,
30
39
  productId,
40
+ setCancellationPolicyId,
31
41
  onPromoChangesAvailability,
32
42
  t,
33
43
  }: UsePrivateShuttlePromoControllerParams) {
@@ -37,6 +47,8 @@ export function usePrivateShuttlePromoController({
37
47
  );
38
48
  const [promoCodeError, setPromoCodeError] = useState('');
39
49
  const [promoCodeValidating, setPromoCodeValidating] = useState(false);
50
+ const [forcedCancellationPolicy, setForcedCancellationPolicy] =
51
+ useState<PrivateShuttleForcedCancellationPolicy>(null);
40
52
  const lastValidatedInputRef = useRef<string | null>(null);
41
53
 
42
54
  const activePromoCode = isAdmin ? appliedPromoCode : null;
@@ -64,9 +76,21 @@ export function usePrivateShuttlePromoController({
64
76
  productCompanyId,
65
77
  productId,
66
78
  hasOngoingDiscount,
79
+ selectedAvailability?.dateTime,
67
80
  );
68
81
  if (result.valid) {
69
82
  setAppliedPromoCode(code);
83
+ if (result.forcedCancellationPolicyId && result.forcedCancellationPolicyLabel) {
84
+ setForcedCancellationPolicy({
85
+ id: result.forcedCancellationPolicyId,
86
+ label: result.forcedCancellationPolicyLabel,
87
+ refundTiers: result.forcedCancellationPolicyRefundTiers,
88
+ changeWindowHoursBefore: result.forcedChangeWindowHoursBefore ?? undefined,
89
+ });
90
+ setCancellationPolicyId(result.forcedCancellationPolicyId);
91
+ } else {
92
+ setForcedCancellationPolicy(null);
93
+ }
70
94
  onPromoChangesAvailability();
71
95
  } else {
72
96
  setPromoCodeError(
@@ -87,6 +111,8 @@ export function usePrivateShuttlePromoController({
87
111
  productCompanyId,
88
112
  productId,
89
113
  hasOngoingDiscount,
114
+ selectedAvailability?.dateTime,
115
+ setCancellationPolicyId,
90
116
  t,
91
117
  onPromoChangesAvailability,
92
118
  ]);
@@ -102,10 +128,12 @@ export function usePrivateShuttlePromoController({
102
128
  const handleRemovePromo = useCallback(() => {
103
129
  lastValidatedInputRef.current = null;
104
130
  setAppliedPromoCode(null);
131
+ setForcedCancellationPolicy(null);
132
+ setCancellationPolicyId(null);
105
133
  setPromoCodeInput('');
106
134
  setPromoCodeError('');
107
135
  onPromoChangesAvailability();
108
- }, [onPromoChangesAvailability]);
136
+ }, [setCancellationPolicyId, onPromoChangesAvailability]);
109
137
 
110
138
  useEffect(() => {
111
139
  if (!isAdmin) return;
@@ -136,6 +164,7 @@ export function usePrivateShuttlePromoController({
136
164
  activePromoCode,
137
165
  promoCodeError,
138
166
  promoCodeValidating,
167
+ forcedCancellationPolicy,
139
168
  handlePromoInputChange,
140
169
  handleApplyPromo,
141
170
  handleRemovePromo,