@ticketboothapp/booking 1.2.25-rc.0 → 1.2.27
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 +11 -29
- package/src/components/booking/AddOnsSection.tsx +2 -2
- package/src/components/booking/AdminPaymentChoiceModal.tsx +1 -1
- package/src/components/booking/BookingDialog.tsx +31 -13
- package/src/components/booking/BookingFlow.tsx +32 -27
- package/src/components/booking/BookingFlowCollage.tsx +10 -6
- package/src/components/booking/BookingFlowPlaceholder.tsx +1 -1
- package/src/components/booking/BookingFlowPreview.tsx +18 -9
- package/src/components/booking/BookingProductGrid.tsx +55 -19
- package/src/components/booking/Calendar.module.css +19 -4
- package/src/components/booking/Calendar.tsx +13 -8
- package/src/components/booking/CancellationPolicySelector.tsx +2 -2
- package/src/components/booking/ChangeBookingDialog.tsx +22 -12
- package/src/components/booking/CheckoutForm.module.css +10 -0
- package/src/components/booking/CheckoutForm.tsx +10 -2
- package/src/components/booking/CheckoutModal.tsx +16 -14
- package/src/components/booking/DapFlowCollage.tsx +5 -2
- package/src/components/booking/DapTourDescription.tsx +4 -4
- package/src/components/booking/DependentAddOnBookingDialog.tsx +23 -16
- package/src/components/booking/DependentAddOnPaymentForm.tsx +10 -7
- package/src/components/booking/ItineraryBox.tsx +6 -6
- package/src/components/booking/ItineraryBuilder.tsx +1 -1
- package/src/components/booking/MealDrinkAddOnSelector.tsx +3 -3
- package/src/components/booking/PickupLocationSelector.tsx +20 -18
- package/src/components/booking/PickupTimeSelector.tsx +3 -3
- package/src/components/booking/PriceBreakdown.tsx +5 -5
- package/src/components/booking/PriceSummary.module.css +7 -0
- package/src/components/booking/PriceSummary.tsx +8 -7
- package/src/components/booking/PrivateShuttleBookingFlow.tsx +28 -19
- package/src/components/booking/PromoCodeInput.module.css +31 -25
- package/src/components/booking/PromoCodeInput.tsx +36 -24
- package/src/components/booking/ReturnTimeSelector.tsx +3 -3
- package/src/components/booking/TermsAcceptance.tsx +7 -2
- package/src/components/booking/TicketSelector.tsx +1 -1
- package/src/components/booking/TourDescription.tsx +11 -6
- package/src/components/booking/booking-flow.css +65 -4
- package/src/hooks/useBookingSourceMetadataFromLocation.ts +1 -1
- package/src/hooks/useIsBookingLaunchLive.ts +1 -1
- package/src/index.ts +26 -64
- package/src/providers/booking-dialog-provider.tsx +62 -53
- package/src/runtime/BookingHostContext.tsx +39 -0
- package/src/runtime/index.ts +13 -0
- package/src/runtime/types.ts +86 -0
- package/tsconfig.json +3 -5
- package/src/assets/icons/minus.svg +0 -7
- package/src/assets/icons/partner-logos/getyourguide.svg +0 -8
- package/src/assets/icons/plus.svg +0 -3
- package/src/colours.css +0 -23
- package/src/components/BookingDetails.module.css +0 -1591
- package/src/components/BookingDetails.tsx +0 -2264
- package/src/components/BookingWidget.tsx +0 -302
- package/src/components/ManageBookingView.tsx +0 -437
- package/src/components/PhoneInputWithCountry.module.css +0 -131
- package/src/components/PhoneInputWithCountry.tsx +0 -44
- package/src/components/PickupLocationDialog.module.css +0 -360
- package/src/components/PickupLocationDialog.tsx +0 -357
- package/src/components/PostBookingDependentAddOnUpsell.module.css +0 -174
- package/src/components/PostBookingDependentAddOnUpsell.tsx +0 -407
- package/src/components/button.css +0 -245
- package/src/components/button.tsx +0 -152
- package/src/components/colorable-svg.tsx +0 -29
- package/src/components/image.css +0 -29
- package/src/components/image.tsx +0 -113
- package/src/components/partner/PartnerBookingPage.module.css +0 -130
- package/src/components/partner/PartnerBookingPage.tsx +0 -390
- package/src/components/partner/PartnerBookingPageWithBrowserMetadata.tsx +0 -45
- package/src/components/product-tag.module.css +0 -30
- package/src/components/product-tag.tsx +0 -34
- package/src/components/product-theme-pages/image-modal.tsx +0 -248
- package/src/components/product-theme-pages/photo-gallery.module.css +0 -200
- package/src/components/terms/TermsContent.tsx +0 -178
- package/src/components/value-pill.module.css +0 -59
- package/src/components/value-pill.tsx +0 -46
- package/src/constants/images.ts +0 -556
- package/src/constants/pill-values.ts +0 -210
- package/src/constants/products.ts +0 -155
- package/src/contexts/AvailabilitiesCacheContext.tsx +0 -125
- package/src/contexts/CompanyContext.tsx +0 -70
- package/src/data/dap-descriptions/session-couples-families-friends.en.json +0 -61
- package/src/data/dap-descriptions/session-elopements.en.json +0 -60
- package/src/data/dap-descriptions/session-proposals.en.json +0 -60
- package/src/data/product-descriptions/afternoon-delight.en.json +0 -35
- package/src/data/product-descriptions/emerald-lake-escape.en.json +0 -68
- package/src/data/product-descriptions/lake-louise-adventure.en.json +0 -74
- package/src/data/product-descriptions/moraine-lake-adventure.en.json +0 -78
- package/src/data/product-descriptions/moraine-lake-sunrise-lake-louise-golden-hour.en.json +0 -65
- package/src/data/product-descriptions/moraine-lake-sunrise.en.json +0 -64
- package/src/data/product-descriptions/private-tour.en.json +0 -80
- package/src/data/product-descriptions/two-lakes-combo.en.json +0 -65
- package/src/data/products-config.json +0 -101
- package/src/lib/analytics.ts +0 -197
- package/src/lib/booking/booking-source.ts +0 -51
- package/src/lib/booking/checkout-breakdown.ts +0 -69
- package/src/lib/booking/correlation-id.ts +0 -46
- package/src/lib/booking/i18n/config.ts +0 -21
- package/src/lib/booking/i18n/index.tsx +0 -144
- package/src/lib/booking/i18n/messages/en.json +0 -236
- package/src/lib/booking/i18n/messages/fr.json +0 -236
- package/src/lib/booking/itinerary-display.ts +0 -36
- package/src/lib/booking/itinerary-labels.ts +0 -70
- package/src/lib/booking/location-calculations.ts +0 -43
- package/src/lib/booking/location-utils.ts +0 -165
- package/src/lib/booking/map-utils.ts +0 -153
- package/src/lib/booking/marker-icons.ts +0 -113
- package/src/lib/booking/normalize-booking-product-id.ts +0 -21
- package/src/lib/booking/pickup-location-types.ts +0 -25
- package/src/lib/booking/places-api.ts +0 -154
- package/src/lib/booking/pricing.ts +0 -466
- package/src/lib/booking/product-option-id.ts +0 -35
- package/src/lib/booking/source-metadata.ts +0 -226
- package/src/lib/booking/sunday-week.ts +0 -14
- package/src/lib/booking/theme.ts +0 -83
- package/src/lib/booking/trace-context.ts +0 -62
- package/src/lib/booking/utils.ts +0 -9
- package/src/lib/booking-api.ts +0 -1793
- package/src/lib/booking-constants.ts +0 -23
- package/src/lib/booking-ref.ts +0 -13
- package/src/lib/booking-types.ts +0 -36
- package/src/lib/currency.ts +0 -81
- package/src/lib/dap-descriptions.ts +0 -50
- package/src/lib/dap-itinerary-preview.ts +0 -315
- package/src/lib/dependent-add-on-api.ts +0 -434
- package/src/lib/env.ts +0 -96
- package/src/lib/firebase.ts +0 -20
- package/src/lib/job-application-api.ts +0 -83
- package/src/lib/manage-booking-embed-print.ts +0 -16
- package/src/lib/manage-booking-post-checkout.ts +0 -68
- package/src/lib/photo-dap-config.ts +0 -228
- package/src/lib/photo-packages.ts +0 -75
- package/src/lib/pickup/map-utils.ts +0 -56
- package/src/lib/pickup/marker-icons.ts +0 -19
- package/src/lib/product-descriptions.ts +0 -66
- package/src/lib/products-config.ts +0 -73
- package/src/providers/dependent-add-on-dialog-provider.tsx +0 -105
- package/src/radius.css +0 -5
- package/src/spacing.css +0 -7
- package/src/strings/en.json +0 -1774
- package/src/strings/es.json +0 -1573
- package/src/strings/fr.json +0 -1573
- package/src/strings/index.js +0 -23
- package/src/text-style.css +0 -56
- package/src/utils/currency-converter.ts +0 -101
|
@@ -23,23 +23,23 @@ import {
|
|
|
23
23
|
isInsufficientCapacityReserveError,
|
|
24
24
|
describePrivateShuttleCapacityConflictMessage,
|
|
25
25
|
reportReserveCapacityConflictClientContext,
|
|
26
|
-
} from '
|
|
26
|
+
} from '../../../../../src/lib/booking-api';
|
|
27
27
|
import {
|
|
28
28
|
EARLIEST_AVAILABILITY_DATE,
|
|
29
29
|
LATEST_AVAILABILITY_DATE,
|
|
30
30
|
INITIAL_FETCH_WEEKS,
|
|
31
|
-
} from '
|
|
32
|
-
import { formatCurrencyAmount } from '
|
|
33
|
-
import { formatBookingRefForDisplay } from '
|
|
34
|
-
import { buildCheckoutBreakdown } from '
|
|
35
|
-
import type { PricingConfig, PrecomputedPricesByCategory } from '
|
|
31
|
+
} from '../../../../../src/lib/booking-constants';
|
|
32
|
+
import { formatCurrencyAmount } from '../../../../../src/lib/currency';
|
|
33
|
+
import { formatBookingRefForDisplay } from '../../../../../src/lib/booking-ref';
|
|
34
|
+
import { buildCheckoutBreakdown } from '../../../../../src/lib/booking/checkout-breakdown';
|
|
35
|
+
import type { PricingConfig, PrecomputedPricesByCategory } from '../../../../../src/lib/booking-api';
|
|
36
36
|
import { Calendar } from './Calendar';
|
|
37
37
|
import { PickupLocationSelector } from './PickupLocationSelector';
|
|
38
|
-
import { useTranslations, useLocale } from '
|
|
38
|
+
import { useTranslations, useLocale } from '../../../../../src/lib/booking/i18n';
|
|
39
39
|
import { type Currency } from './CurrencySwitcher';
|
|
40
|
-
import { useCompanyTimezone } from '
|
|
41
|
-
import { useBookingApp } from '
|
|
42
|
-
import { useAvailabilitiesCache, buildAvailabilitiesCacheKey } from '
|
|
40
|
+
import { useCompanyTimezone } from '../../../../../src/contexts/CompanyContext';
|
|
41
|
+
import { useBookingApp } from '../../contexts/BookingAppContext';
|
|
42
|
+
import { useAvailabilitiesCache, buildAvailabilitiesCacheKey } from '../../../../../src/contexts/AvailabilitiesCacheContext';
|
|
43
43
|
import { CheckoutModal, type CheckoutModalLineItem } from './CheckoutModal';
|
|
44
44
|
import { CancellationPolicySelector } from './CancellationPolicySelector';
|
|
45
45
|
import { PriceSummary } from './PriceSummary';
|
|
@@ -49,18 +49,16 @@ import { MealDrinkAddOnSelector, canUseMealDrinkSelector } from './MealDrinkAddO
|
|
|
49
49
|
import { AdminPaymentChoiceModal } from './AdminPaymentChoiceModal';
|
|
50
50
|
import { BookingFlowCollage } from './BookingFlowCollage';
|
|
51
51
|
import { TourDescription } from './TourDescription';
|
|
52
|
-
import {
|
|
53
|
-
import { getProducts } from '@/constants/products';
|
|
54
|
-
import defaultStrings from '@/strings';
|
|
55
|
-
import { trackViewItem } from '@/lib/analytics';
|
|
52
|
+
import { useBookingHost } from '../../runtime';
|
|
56
53
|
import styles from './PrivateShuttleBookingFlow.module.css';
|
|
57
54
|
import {
|
|
58
55
|
buildBookingSourceContext,
|
|
59
56
|
inferClientBookingSourceFromProductIds,
|
|
60
57
|
type BookingSourceMetadata,
|
|
61
|
-
} from '
|
|
58
|
+
} from '../../../../../src/lib/booking/source-metadata';
|
|
59
|
+
import type { VideoSources } from '../../../../../src/constants/products';
|
|
62
60
|
import type { BookingFlowUiOptions } from './booking-flow-ui';
|
|
63
|
-
import { BOOKING_FLOW_ABANDON_EVENT } from '
|
|
61
|
+
import { BOOKING_FLOW_ABANDON_EVENT } from '../../providers/booking-dialog-provider';
|
|
64
62
|
|
|
65
63
|
interface PrivateShuttleBookingFlowProps {
|
|
66
64
|
product: Product;
|
|
@@ -154,6 +152,7 @@ export function PrivateShuttleBookingFlow({
|
|
|
154
152
|
availabilityCancellationPolicyProfileId,
|
|
155
153
|
initialValues,
|
|
156
154
|
}: PrivateShuttleBookingFlowProps) {
|
|
155
|
+
const { strings: defaultStrings, analytics, catalog } = useBookingHost();
|
|
157
156
|
const { t } = useTranslations();
|
|
158
157
|
const { locale } = useLocale();
|
|
159
158
|
const companyTimezone = useCompanyTimezone();
|
|
@@ -296,7 +295,7 @@ export function PrivateShuttleBookingFlow({
|
|
|
296
295
|
hasFiredViewItem.current = true;
|
|
297
296
|
const id = productId || product.productId;
|
|
298
297
|
const price = product.minPriceByCurrency?.[currency] ?? 0;
|
|
299
|
-
trackViewItem(id, product.name, price, currency);
|
|
298
|
+
analytics.trackViewItem(id, product.name, price, currency);
|
|
300
299
|
}
|
|
301
300
|
}, [product, productId, currency]);
|
|
302
301
|
|
|
@@ -1773,8 +1772,18 @@ export function PrivateShuttleBookingFlow({
|
|
|
1773
1772
|
);
|
|
1774
1773
|
}
|
|
1775
1774
|
|
|
1776
|
-
const config = productId
|
|
1777
|
-
|
|
1775
|
+
const config = productId
|
|
1776
|
+
? (catalog.getProductByIdOrSlug(productId) as {
|
|
1777
|
+
display?: {
|
|
1778
|
+
collageImageIds?: string[];
|
|
1779
|
+
imageIds?: string[];
|
|
1780
|
+
};
|
|
1781
|
+
} | null)
|
|
1782
|
+
: null;
|
|
1783
|
+
const displayProducts = catalog.getProducts(defaultStrings) as Record<
|
|
1784
|
+
string,
|
|
1785
|
+
{ id: string; videoUrl?: VideoSources }
|
|
1786
|
+
>;
|
|
1778
1787
|
const displayProduct = Object.values(displayProducts).find((p) => p.id === productId);
|
|
1779
1788
|
const collageImageIds = config?.display?.collageImageIds ?? config?.display?.imageIds ?? [];
|
|
1780
1789
|
const hasVideo = !!displayProduct?.videoUrl;
|
|
@@ -67,13 +67,13 @@
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/*
|
|
70
|
+
/* Single bordered field — icons overlay the right padding (original look, no nested box). */
|
|
71
71
|
.input {
|
|
72
72
|
width: 100%;
|
|
73
73
|
min-width: 0;
|
|
74
|
-
padding: 0.
|
|
74
|
+
padding: 0.5rem 0.625rem;
|
|
75
75
|
font-size: 0.875rem;
|
|
76
|
-
border-radius: 0.
|
|
76
|
+
border-radius: 0.5rem;
|
|
77
77
|
border: 1px solid var(--booking-stone-300, #d6d3d1);
|
|
78
78
|
background: #fff;
|
|
79
79
|
color: var(--booking-stone-900, #1c1917);
|
|
@@ -85,20 +85,37 @@
|
|
|
85
85
|
border-color: var(--booking-stone-500, #78716c);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
.
|
|
88
|
+
.inputMuted {
|
|
89
89
|
background: var(--booking-stone-50, #fafaf9);
|
|
90
90
|
cursor: default;
|
|
91
|
-
padding-right: 3.5rem;
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
.inputPadSingle {
|
|
94
|
+
padding-right: 2rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.inputPadPair {
|
|
98
|
+
padding-right: 3.375rem;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.inputSuffix {
|
|
96
102
|
position: absolute;
|
|
97
103
|
right: 0.25rem;
|
|
98
104
|
top: 50%;
|
|
99
105
|
transform: translateY(-50%);
|
|
100
|
-
|
|
101
|
-
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
gap: 0.125rem;
|
|
109
|
+
pointer-events: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.inputSuffix button {
|
|
113
|
+
pointer-events: auto;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.removeBtn {
|
|
117
|
+
width: 1.375rem;
|
|
118
|
+
height: 1.375rem;
|
|
102
119
|
padding: 0;
|
|
103
120
|
border-radius: 9999px;
|
|
104
121
|
color: var(--booking-stone-500, #78716c);
|
|
@@ -108,6 +125,7 @@
|
|
|
108
125
|
display: flex;
|
|
109
126
|
align-items: center;
|
|
110
127
|
justify-content: center;
|
|
128
|
+
flex-shrink: 0;
|
|
111
129
|
}
|
|
112
130
|
|
|
113
131
|
.removeBtn:hover {
|
|
@@ -116,15 +134,12 @@
|
|
|
116
134
|
}
|
|
117
135
|
|
|
118
136
|
.loading {
|
|
119
|
-
position: absolute;
|
|
120
|
-
right: 0.5rem;
|
|
121
|
-
top: 50%;
|
|
122
|
-
transform: translateY(-50%);
|
|
123
137
|
width: 1.25rem;
|
|
124
138
|
height: 1.25rem;
|
|
125
139
|
border-radius: 9999px;
|
|
126
140
|
background: var(--booking-stone-100, #f5f5f4);
|
|
127
141
|
animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
142
|
+
flex-shrink: 0;
|
|
128
143
|
}
|
|
129
144
|
|
|
130
145
|
@keyframes pulse {
|
|
@@ -133,10 +148,6 @@
|
|
|
133
148
|
}
|
|
134
149
|
|
|
135
150
|
.errorIcon {
|
|
136
|
-
position: absolute;
|
|
137
|
-
right: 0.5rem;
|
|
138
|
-
top: 50%;
|
|
139
|
-
transform: translateY(-50%);
|
|
140
151
|
width: 1.25rem;
|
|
141
152
|
height: 1.25rem;
|
|
142
153
|
border-radius: 9999px;
|
|
@@ -145,16 +156,12 @@
|
|
|
145
156
|
display: flex;
|
|
146
157
|
align-items: center;
|
|
147
158
|
justify-content: center;
|
|
159
|
+
flex-shrink: 0;
|
|
148
160
|
}
|
|
149
161
|
|
|
150
|
-
/* Check icon - inside box, next to remove btn */
|
|
151
162
|
.appliedBadge {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
top: 50%;
|
|
155
|
-
transform: translateY(-50%);
|
|
156
|
-
width: 1.5rem;
|
|
157
|
-
height: 1.5rem;
|
|
163
|
+
width: 1.375rem;
|
|
164
|
+
height: 1.375rem;
|
|
158
165
|
border-radius: 9999px;
|
|
159
166
|
background: var(--booking-emerald-500, #10b981);
|
|
160
167
|
color: #fff;
|
|
@@ -163,4 +170,3 @@
|
|
|
163
170
|
justify-content: center;
|
|
164
171
|
flex-shrink: 0;
|
|
165
172
|
}
|
|
166
|
-
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Check, X } from 'lucide-react';
|
|
4
|
-
import { formatCurrencyAmount } from '
|
|
4
|
+
import { formatCurrencyAmount } from '../../../../../src/lib/currency';
|
|
5
5
|
import type { Currency } from './CurrencySwitcher';
|
|
6
6
|
import styles from './PromoCodeInput.module.css';
|
|
7
7
|
|
|
@@ -36,6 +36,11 @@ export function PromoCodeInput({
|
|
|
36
36
|
onRemove,
|
|
37
37
|
locked = false,
|
|
38
38
|
}: PromoCodeInputProps) {
|
|
39
|
+
const showSuffix = !!(appliedPromoCode || promoCodeValidating || promoCodeError);
|
|
40
|
+
const suffixPadding =
|
|
41
|
+
appliedPromoCode && !locked ? styles.inputPadPair : showSuffix ? styles.inputPadSingle : '';
|
|
42
|
+
const inputMuted = locked || appliedPromoCode ? styles.inputMuted : '';
|
|
43
|
+
|
|
39
44
|
return (
|
|
40
45
|
<div className={styles.promoRow}>
|
|
41
46
|
<label htmlFor="booking-promo-code" className={styles.promoLabel}>
|
|
@@ -59,31 +64,38 @@ export function PromoCodeInput({
|
|
|
59
64
|
autoComplete="off"
|
|
60
65
|
readOnly={locked || !!appliedPromoCode}
|
|
61
66
|
disabled={locked}
|
|
62
|
-
className={styles.input}
|
|
67
|
+
className={[styles.input, suffixPadding, inputMuted].filter(Boolean).join(' ')}
|
|
63
68
|
/>
|
|
64
|
-
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
{showSuffix && (
|
|
70
|
+
<div className={styles.inputSuffix}>
|
|
71
|
+
{appliedPromoCode ? (
|
|
72
|
+
<>
|
|
73
|
+
<span
|
|
74
|
+
className={styles.appliedBadge}
|
|
75
|
+
aria-label={t('booking.promoApplied', { code: appliedPromoCode })}
|
|
76
|
+
>
|
|
77
|
+
<Check className="w-3.5 h-3.5" strokeWidth={3} />
|
|
78
|
+
</span>
|
|
79
|
+
{!locked && (
|
|
80
|
+
<button
|
|
81
|
+
type="button"
|
|
82
|
+
onClick={onRemove}
|
|
83
|
+
className={styles.removeBtn}
|
|
84
|
+
aria-label={t('booking.removePromo')}
|
|
85
|
+
>
|
|
86
|
+
<X className="w-4 h-4" strokeWidth={2.5} />
|
|
87
|
+
</button>
|
|
88
|
+
)}
|
|
89
|
+
</>
|
|
90
|
+
) : promoCodeValidating ? (
|
|
91
|
+
<span className={styles.loading} aria-hidden />
|
|
92
|
+
) : (
|
|
93
|
+
<span className={styles.errorIcon} aria-label={promoCodeError}>
|
|
94
|
+
<X className="w-3 h-3" strokeWidth={3} />
|
|
95
|
+
</span>
|
|
78
96
|
)}
|
|
79
|
-
|
|
80
|
-
)
|
|
81
|
-
<span className={styles.loading} aria-hidden />
|
|
82
|
-
) : promoCodeError ? (
|
|
83
|
-
<span className={styles.errorIcon} aria-label={promoCodeError}>
|
|
84
|
-
<X className="w-3 h-3" strokeWidth={3} />
|
|
85
|
-
</span>
|
|
86
|
-
) : null}
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
87
99
|
</div>
|
|
88
100
|
{promoDiscountAmount > 0 && (
|
|
89
101
|
<span className={styles.promoDiscount}>
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { parseISO } from 'date-fns';
|
|
4
4
|
import { formatInTimeZone } from 'date-fns-tz';
|
|
5
|
-
import { formatCurrencyAmount } from '
|
|
6
|
-
import type { ReturnOption } from '
|
|
5
|
+
import { formatCurrencyAmount } from '../../../../../src/lib/currency';
|
|
6
|
+
import type { ReturnOption } from '../../../../../src/lib/booking-api';
|
|
7
7
|
import type { Currency } from './CurrencySwitcher';
|
|
8
8
|
import styles from './ReturnTimeSelector.module.css';
|
|
9
9
|
|
|
@@ -122,7 +122,7 @@ export function ReturnTimeSelector({
|
|
|
122
122
|
{t('booking.soldOut')}
|
|
123
123
|
</div>
|
|
124
124
|
)}
|
|
125
|
-
{isInsufficientForParty && !isAdmin && (
|
|
125
|
+
{isInsufficientForParty && !isSoldOut && !isAdmin && (
|
|
126
126
|
<div className={styles.soldOut}>
|
|
127
127
|
{`Only ${returnOption.vacancies} spot${returnOption.vacancies === 1 ? '' : 's'} left, decrease your ticket count below to select this time`}
|
|
128
128
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { useBookingHost } from '../../runtime';
|
|
5
5
|
|
|
6
6
|
export interface TermsAcceptanceProps {
|
|
7
7
|
checked: boolean;
|
|
@@ -24,8 +24,13 @@ export function TermsAcceptance({
|
|
|
24
24
|
t,
|
|
25
25
|
termsContent,
|
|
26
26
|
}: TermsAcceptanceProps) {
|
|
27
|
+
const { slots } = useBookingHost();
|
|
28
|
+
const TermsContent = slots.TermsContent;
|
|
27
29
|
const [modalOpen, setModalOpen] = useState(false);
|
|
28
|
-
const content =
|
|
30
|
+
const content =
|
|
31
|
+
termsContent ?? (
|
|
32
|
+
<TermsContent className="text-xs [&_h1]:text-base [&_h2]:text-sm [&_h3]:text-xs [&_p]:text-xs" />
|
|
33
|
+
);
|
|
29
34
|
|
|
30
35
|
return (
|
|
31
36
|
<>
|
|
@@ -4,7 +4,7 @@ import { useMemo } from 'react';
|
|
|
4
4
|
import { parseISO } from 'date-fns';
|
|
5
5
|
import { enUS, fr } from 'date-fns/locale';
|
|
6
6
|
import { formatInTimeZone } from 'date-fns-tz';
|
|
7
|
-
import { formatCurrencyAmount } from '
|
|
7
|
+
import { formatCurrencyAmount } from '../../../../../src/lib/currency';
|
|
8
8
|
import type { Currency } from './CurrencySwitcher';
|
|
9
9
|
import styles from './TicketSelector.module.css';
|
|
10
10
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState, useMemo } from 'react';
|
|
4
|
-
import { useTranslations, useLocale } from '
|
|
5
|
-
import {
|
|
6
|
-
import PlusIcon from '@/assets/icons/plus.svg';
|
|
7
|
-
import MinusIcon from '@/assets/icons/minus.svg';
|
|
4
|
+
import { useTranslations, useLocale } from '../../../../../src/lib/booking/i18n';
|
|
5
|
+
import { useBookingHost } from '../../runtime';
|
|
8
6
|
import styles from './TourDescription.module.css';
|
|
9
7
|
|
|
10
8
|
export interface TourDescriptionReview {
|
|
@@ -126,6 +124,9 @@ export function TourDescription({
|
|
|
126
124
|
sections: sectionsProp,
|
|
127
125
|
defaultExpanded = false,
|
|
128
126
|
}: TourDescriptionProps) {
|
|
127
|
+
const { catalog, slots } = useBookingHost();
|
|
128
|
+
const PlusIcon = slots.PlusIcon;
|
|
129
|
+
const MinusIcon = slots.MinusIcon;
|
|
129
130
|
const { t } = useTranslations();
|
|
130
131
|
const { locale: contextLocale } = useLocale();
|
|
131
132
|
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
|
@@ -135,10 +136,14 @@ export function TourDescription({
|
|
|
135
136
|
|
|
136
137
|
const loadedContent = useMemo(() => {
|
|
137
138
|
if (productSlug && locale) {
|
|
138
|
-
return getProductDescription(productSlug, locale)
|
|
139
|
+
return catalog.getProductDescription(productSlug, locale) as {
|
|
140
|
+
paragraphs?: string[];
|
|
141
|
+
review?: TourDescriptionReview;
|
|
142
|
+
sections?: TourDescriptionSection[];
|
|
143
|
+
} | null;
|
|
139
144
|
}
|
|
140
145
|
return null;
|
|
141
|
-
}, [productSlug, locale]);
|
|
146
|
+
}, [productSlug, locale, catalog]);
|
|
142
147
|
|
|
143
148
|
const paragraphs = paragraphsProp ?? loadedContent?.paragraphs ?? [];
|
|
144
149
|
const review = reviewProp ?? loadedContent?.review;
|
|
@@ -860,14 +860,37 @@
|
|
|
860
860
|
}
|
|
861
861
|
|
|
862
862
|
/* ========== Ticket +/- buttons - round ========== */
|
|
863
|
+
/* Explicit border/background: partner portal (HeroUI/host) can strip CSS-module button chrome; higher specificity below. */
|
|
863
864
|
.booking-flow-preflight [class*="TicketSelector_qtyBtnDecrement"],
|
|
864
|
-
.booking-flow-preflight [class*="TicketSelector_qtyBtnIncrement"]
|
|
865
|
+
.booking-flow-preflight [class*="TicketSelector_qtyBtnIncrement"],
|
|
866
|
+
.booking-flow-root [class*="TicketSelector_qtyBtnDecrement"]:not(:disabled),
|
|
867
|
+
.booking-flow-root [class*="TicketSelector_qtyBtnIncrement"]:not(:disabled) {
|
|
865
868
|
width: 2.5rem !important;
|
|
866
869
|
height: 2.5rem !important;
|
|
867
870
|
border-radius: 9999px !important;
|
|
868
871
|
display: flex !important;
|
|
869
872
|
align-items: center !important;
|
|
870
873
|
justify-content: center !important;
|
|
874
|
+
border: 1px solid var(--booking-stone-300, #d6d3d1) !important;
|
|
875
|
+
background-color: #fff !important;
|
|
876
|
+
color: var(--booking-stone-600, #57534e) !important;
|
|
877
|
+
-webkit-appearance: none !important;
|
|
878
|
+
appearance: none !important;
|
|
879
|
+
}
|
|
880
|
+
.booking-flow-preflight [class*="TicketSelector_qtyBtnDecrement"]:disabled,
|
|
881
|
+
.booking-flow-preflight [class*="TicketSelector_qtyBtnIncrement"]:disabled,
|
|
882
|
+
.booking-flow-root [class*="TicketSelector_qtyBtnDecrement"]:disabled,
|
|
883
|
+
.booking-flow-root [class*="TicketSelector_qtyBtnIncrement"]:disabled {
|
|
884
|
+
border: 1px solid var(--booking-stone-200, #e7e5e4) !important;
|
|
885
|
+
background-color: var(--booking-stone-100, #f5f5f4) !important;
|
|
886
|
+
color: var(--booking-stone-400, #a8a29e) !important;
|
|
887
|
+
opacity: 0.5 !important;
|
|
888
|
+
}
|
|
889
|
+
.booking-flow-preflight [class*="TicketSelector_qtyBtnOverbook"],
|
|
890
|
+
.booking-flow-root [class*="TicketSelector_qtyBtnOverbook"] {
|
|
891
|
+
border: 2px solid #ef4444 !important;
|
|
892
|
+
background-color: #fee2e2 !important;
|
|
893
|
+
color: #b91c1c !important;
|
|
871
894
|
}
|
|
872
895
|
|
|
873
896
|
/* ========== Deposit notice - keep text small (preflight can inherit large sizes) ========== */
|
|
@@ -876,7 +899,7 @@
|
|
|
876
899
|
font-size: 0.75rem !important;
|
|
877
900
|
}
|
|
878
901
|
|
|
879
|
-
/* ========== Receipt
|
|
902
|
+
/* ========== Receipt / utilities: restore Tailwind border widths inside scoped preflight ========== */
|
|
880
903
|
.booking-flow-preflight .border-t {
|
|
881
904
|
border-top-width: 1px !important;
|
|
882
905
|
}
|
|
@@ -884,12 +907,50 @@
|
|
|
884
907
|
border-bottom-width: 1px !important;
|
|
885
908
|
}
|
|
886
909
|
|
|
910
|
+
/* PriceSummary.ruleAbove — explicit module border beats preflight `* { border-width: 0 }` */
|
|
911
|
+
.booking-flow-preflight [class*="PriceSummary_ruleAbove"],
|
|
912
|
+
.booking-flow-root [class*="PriceSummary_ruleAbove"] {
|
|
913
|
+
border-top-width: 1px !important;
|
|
914
|
+
border-top-style: solid !important;
|
|
915
|
+
border-top-color: var(--booking-stone-200, #e7e5e4) !important;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/* Line above first receipt row (above ADULT etc.): CheckoutForm / private shuttle section wrappers */
|
|
919
|
+
.booking-flow-preflight [class*="CheckoutForm_section"],
|
|
920
|
+
.booking-flow-root [class*="CheckoutForm_section"],
|
|
921
|
+
.booking-flow-preflight [class*="PrivateShuttleBookingFlow_section"],
|
|
922
|
+
.booking-flow-root [class*="PrivateShuttleBookingFlow_section"] {
|
|
923
|
+
border-top-width: 1px !important;
|
|
924
|
+
border-top-style: solid !important;
|
|
925
|
+
border-top-color: var(--booking-stone-200, #e7e5e4) !important;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/* Line above “Promo / voucher / gift card” — PromoCodeInput.promoRow */
|
|
929
|
+
.booking-flow-preflight [class*="PromoCodeInput_promoRow"],
|
|
930
|
+
.booking-flow-root [class*="PromoCodeInput_promoRow"] {
|
|
931
|
+
border-top-width: 1px !important;
|
|
932
|
+
border-top-style: solid !important;
|
|
933
|
+
border-top-color: var(--booking-stone-200, #e7e5e4) !important;
|
|
934
|
+
}
|
|
935
|
+
|
|
887
936
|
/* ========== Input fields - padding ========== */
|
|
888
937
|
.booking-flow-preflight [class*="CheckoutForm_input"] {
|
|
889
938
|
padding: 0.875rem 1.25rem !important;
|
|
890
939
|
}
|
|
891
|
-
|
|
892
|
-
|
|
940
|
+
/* Promo field: scope to <input> only — [class*="PromoCodeInput_input"] also matches inputWrap (…inputWrap…) and was drawing a second box */
|
|
941
|
+
.booking-flow-preflight input[class*="PromoCodeInput_input"] {
|
|
942
|
+
padding: 0.5rem 0.625rem !important;
|
|
943
|
+
border: 1px solid var(--booking-stone-300) !important;
|
|
944
|
+
border-radius: 0.5rem !important;
|
|
945
|
+
}
|
|
946
|
+
.booking-flow-preflight input[class*="PromoCodeInput_inputPadSingle"] {
|
|
947
|
+
padding-right: 2rem !important;
|
|
948
|
+
}
|
|
949
|
+
.booking-flow-preflight input[class*="PromoCodeInput_inputPadPair"] {
|
|
950
|
+
padding-right: 3.375rem !important;
|
|
951
|
+
}
|
|
952
|
+
.booking-flow-preflight input[class*="PromoCodeInput_inputMuted"] {
|
|
953
|
+
background-color: var(--booking-stone-50, #fafaf9) !important;
|
|
893
954
|
}
|
|
894
955
|
/* PrivateShuttleBookingFlow inputs - preflight resets padding (exclude passengerSelect, has its own) */
|
|
895
956
|
.booking-flow-preflight [class*="PrivateShuttleBookingFlow_input"]:not([class*="passengerSelect"]) {
|
|
@@ -5,7 +5,7 @@ import { usePathname } from 'next/navigation';
|
|
|
5
5
|
import {
|
|
6
6
|
buildBookingSourceMetadataFromLocation,
|
|
7
7
|
type BookingSourceMetadata,
|
|
8
|
-
} from '
|
|
8
|
+
} from '../../../../src/lib/booking/source-metadata';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Re-reads URL-derived booking attribution when the **path** changes. Query-only updates on the
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect } from 'react';
|
|
4
|
-
import { BOOKING_LAUNCH_AT } from '
|
|
4
|
+
import { BOOKING_LAUNCH_AT } from '../../../../src/lib/booking-constants';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Returns whether the full booking flow is live (past launch time).
|
package/src/index.ts
CHANGED
|
@@ -11,37 +11,35 @@ export {
|
|
|
11
11
|
type PublicStaffPortalSignInOption,
|
|
12
12
|
} from './public-partners';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
/** Canonical Via Via booking UI — same modules as `@/components/booking/*` on the site. */
|
|
15
|
+
export { BookingFlow } from './components/booking/BookingFlow';
|
|
16
|
+
export { PrivateShuttleBookingFlow } from './components/booking/PrivateShuttleBookingFlow';
|
|
17
|
+
export type { BookingFlowUiOptions } from './components/booking/booking-flow-ui';
|
|
18
|
+
export { default as BookingDialog } from './components/booking/BookingDialog';
|
|
19
|
+
export { default as BookingProductGrid } from './components/booking/BookingProductGrid';
|
|
20
|
+
|
|
21
|
+
export type {
|
|
22
|
+
BookingAppMode,
|
|
23
|
+
ViewerRole,
|
|
24
|
+
BookingAppPermissions,
|
|
25
|
+
ManageParams,
|
|
26
|
+
BookingAppContextValue,
|
|
27
|
+
BookingAppProviderProps,
|
|
23
28
|
} from './contexts/BookingAppContext';
|
|
29
|
+
export { BookingAppProvider, useBookingApp } from './contexts/BookingAppContext';
|
|
24
30
|
|
|
31
|
+
export type {
|
|
32
|
+
BookingRuntimeEnv,
|
|
33
|
+
BookingRuntimeValue,
|
|
34
|
+
BookingRuntimeAnalytics,
|
|
35
|
+
BookingRuntimeSlots,
|
|
36
|
+
BookingRuntimeCatalog,
|
|
37
|
+
} from './runtime/types';
|
|
25
38
|
export {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export {
|
|
31
|
-
CompanyProvider,
|
|
32
|
-
useCompany,
|
|
33
|
-
useCompanyTimezone,
|
|
34
|
-
} from './contexts/CompanyContext';
|
|
35
|
-
|
|
36
|
-
export { type Company } from './lib/booking-api';
|
|
37
|
-
|
|
38
|
-
export {
|
|
39
|
-
AVAILABILITIES_CACHE_TTL_MS,
|
|
40
|
-
AvailabilitiesCacheProvider,
|
|
41
|
-
useAvailabilitiesCache,
|
|
42
|
-
buildAvailabilitiesCacheKey,
|
|
43
|
-
type CachedAvailabilitiesData,
|
|
44
|
-
} from './contexts/AvailabilitiesCacheContext';
|
|
39
|
+
BookingHostProvider,
|
|
40
|
+
useBookingHost,
|
|
41
|
+
useBookingHostOptional,
|
|
42
|
+
} from './runtime';
|
|
45
43
|
|
|
46
44
|
export {
|
|
47
45
|
BookingDialogProvider,
|
|
@@ -53,39 +51,3 @@ export {
|
|
|
53
51
|
type BookingScreen,
|
|
54
52
|
type ProductGridRestoreState,
|
|
55
53
|
} from './providers/booking-dialog-provider';
|
|
56
|
-
|
|
57
|
-
export {
|
|
58
|
-
setPartnerPortalBookingJwtGetter,
|
|
59
|
-
type Product,
|
|
60
|
-
} from './lib/booking-api';
|
|
61
|
-
|
|
62
|
-
export {
|
|
63
|
-
KnownBookingSource,
|
|
64
|
-
DEFAULT_BOOKING_SOURCE,
|
|
65
|
-
PARTNER_PORTAL_BOOKING_SOURCE,
|
|
66
|
-
inferClientBookingSourceFromProductIds,
|
|
67
|
-
mergedMetadataImpliesPartnerPortal,
|
|
68
|
-
isPublicPartnerMarketingPath,
|
|
69
|
-
isDedicatedPartnerBookingPortalHost,
|
|
70
|
-
buildBookingSourceMetadataFromLocation,
|
|
71
|
-
mergeBookingSourceMetadata,
|
|
72
|
-
buildBookingSourceContext,
|
|
73
|
-
type BookingSourceMetadata,
|
|
74
|
-
type BuildBookingSourceContextOptions,
|
|
75
|
-
} from './lib/booking/source-metadata';
|
|
76
|
-
|
|
77
|
-
export {
|
|
78
|
-
default as PartnerBookingPageWithBrowserMetadata,
|
|
79
|
-
type PartnerBookingPageWithBrowserMetadataProps,
|
|
80
|
-
} from './components/partner/PartnerBookingPageWithBrowserMetadata';
|
|
81
|
-
|
|
82
|
-
export {
|
|
83
|
-
BookingWidget,
|
|
84
|
-
type BookingWidgetProps,
|
|
85
|
-
} from './components/BookingWidget';
|
|
86
|
-
|
|
87
|
-
export {
|
|
88
|
-
ManageBookingView,
|
|
89
|
-
type ManageBookingViewProps,
|
|
90
|
-
type StaffBookingAttribution,
|
|
91
|
-
} from './components/ManageBookingView';
|