@ticketboothapp/booking 0.1.18 → 0.1.20

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.
Files changed (152) hide show
  1. package/package.json +1 -1
  2. package/src/components/BookingWidget.tsx +282 -26
  3. package/src/components/ManageBookingView.tsx +75 -23
  4. package/src/components/booking/BookingProductGrid.tsx +1 -1
  5. package/src/components/booking/Calendar.module.css +3 -3
  6. package/src/components/booking/CheckoutForm.tsx +1 -1
  7. package/src/components/booking/PickupLocationSelector.tsx +1 -1
  8. package/src/index.ts +3 -1
  9. package/src/app/photo-sessions/photo-packages.ts +0 -75
  10. package/src/assets/icons/minus.svg +0 -7
  11. package/src/assets/icons/partner-logos/getyourguide.svg +0 -8
  12. package/src/assets/icons/plus.svg +0 -3
  13. package/src/colours.css +0 -23
  14. package/src/components/BookingDetails.module.css +0 -1591
  15. package/src/components/BookingDetails.tsx +0 -2264
  16. package/src/components/JobApplicationDialog.module.css +0 -440
  17. package/src/components/JobApplicationDialog.tsx +0 -620
  18. package/src/components/PhoneInputWithCountry.module.css +0 -131
  19. package/src/components/PhoneInputWithCountry.tsx +0 -44
  20. package/src/components/PickupLocationDialog.module.css +0 -360
  21. package/src/components/PickupLocationDialog.tsx +0 -357
  22. package/src/components/PickupLocationMap.tsx +0 -110
  23. package/src/components/PostBookingDependentAddOnUpsell.module.css +0 -174
  24. package/src/components/PostBookingDependentAddOnUpsell.tsx +0 -407
  25. package/src/components/accordion.css +0 -27
  26. package/src/components/accordion.tsx +0 -29
  27. package/src/components/analytics/AnalyticsConsentRestore.tsx +0 -19
  28. package/src/components/analytics/AnalyticsScripts.tsx +0 -106
  29. package/src/components/analytics/CookieConsentBanner.css +0 -86
  30. package/src/components/analytics/CookieConsentBanner.tsx +0 -102
  31. package/src/components/bottom-sheet.module.css +0 -78
  32. package/src/components/bottom-sheet.tsx +0 -60
  33. package/src/components/breadcrumb.module.css +0 -40
  34. package/src/components/breadcrumb.tsx +0 -36
  35. package/src/components/button.css +0 -245
  36. package/src/components/button.tsx +0 -152
  37. package/src/components/client-bottom-sheet.tsx +0 -14
  38. package/src/components/colorable-svg.tsx +0 -29
  39. package/src/components/conditional-footer.tsx +0 -27
  40. package/src/components/contact-us.module.css +0 -147
  41. package/src/components/contact-us.tsx +0 -49
  42. package/src/components/email-signup.css +0 -151
  43. package/src/components/email-signup.tsx +0 -63
  44. package/src/components/faq-wrapper.module.css +0 -47
  45. package/src/components/faq-wrapper.tsx +0 -15
  46. package/src/components/footer.css +0 -187
  47. package/src/components/footer.tsx +0 -143
  48. package/src/components/global-simple-modal.tsx +0 -33
  49. package/src/components/google-review-summary.module.css +0 -77
  50. package/src/components/google-review-summary.tsx +0 -50
  51. package/src/components/hero-image.css +0 -13
  52. package/src/components/hero-image.tsx +0 -44
  53. package/src/components/image.css +0 -29
  54. package/src/components/image.tsx +0 -113
  55. package/src/components/language-aware-link.tsx +0 -72
  56. package/src/components/language-switcher.module.css +0 -124
  57. package/src/components/language-switcher.tsx +0 -75
  58. package/src/components/map-section.css +0 -59
  59. package/src/components/map-section.tsx +0 -63
  60. package/src/components/navbar.module.css +0 -152
  61. package/src/components/navbar.tsx +0 -125
  62. package/src/components/parallax-provider.tsx +0 -11
  63. package/src/components/product-tag.module.css +0 -30
  64. package/src/components/product-tag.tsx +0 -34
  65. package/src/components/product-theme-pages/best-option.module.css +0 -70
  66. package/src/components/product-theme-pages/best-option.tsx +0 -35
  67. package/src/components/product-theme-pages/extended-tour-options.module.css +0 -22
  68. package/src/components/product-theme-pages/extended-tour-options.tsx +0 -11
  69. package/src/components/product-theme-pages/image-modal.tsx +0 -248
  70. package/src/components/product-theme-pages/photo-gallery.module.css +0 -200
  71. package/src/components/product-theme-pages/photo-gallery.tsx +0 -90
  72. package/src/components/product-theme-pages/product-theme-page-layout.module.css +0 -13
  73. package/src/components/product-theme-pages/product-theme-page-layout.tsx +0 -67
  74. package/src/components/product-theme-pages/top-of-fold.module.css +0 -179
  75. package/src/components/product-theme-pages/top-of-fold.tsx +0 -80
  76. package/src/components/product-tile/image-only-product-tile-desktop.module.css +0 -106
  77. package/src/components/product-tile/image-only-product-tile-desktop.tsx +0 -56
  78. package/src/components/product-tile/image-only-product-tile-mobile.module.css +0 -122
  79. package/src/components/product-tile/image-only-product-tile-mobile.tsx +0 -89
  80. package/src/components/product-tile/image-only-product-tile.tsx +0 -44
  81. package/src/components/product-tile/product-tile-card.module.css +0 -84
  82. package/src/components/product-tile/product-tile-card.tsx +0 -61
  83. package/src/components/review-highlights-section.css +0 -85
  84. package/src/components/review-highlights-section.tsx +0 -127
  85. package/src/components/season-closure-overlay.module.css +0 -99
  86. package/src/components/season-closure-overlay.tsx +0 -98
  87. package/src/components/simple-modal.tsx +0 -69
  88. package/src/components/simple-top-of-fold.module.css +0 -76
  89. package/src/components/simple-top-of-fold.tsx +0 -34
  90. package/src/components/spacer.css +0 -41
  91. package/src/components/spacer.tsx +0 -23
  92. package/src/components/star-rating.module.css +0 -74
  93. package/src/components/star-rating.tsx +0 -48
  94. package/src/components/terms/TermsContent.tsx +0 -178
  95. package/src/components/title-subtitle.module.css +0 -10
  96. package/src/components/title-subtitle.tsx +0 -30
  97. package/src/components/translatable-reviews.tsx +0 -75
  98. package/src/components/value-pill.module.css +0 -59
  99. package/src/components/value-pill.tsx +0 -46
  100. package/src/components/value-props.css +0 -185
  101. package/src/components/value-props.tsx +0 -88
  102. package/src/constants/booking-guide-quiz.ts +0 -64
  103. package/src/constants/contact-info.ts +0 -2
  104. package/src/constants/faq.ts +0 -44
  105. package/src/constants/images.ts +0 -556
  106. package/src/constants/json-ld/faq-json-ld.tsx +0 -170
  107. package/src/constants/json-ld/homepage-json-ld.tsx +0 -138
  108. package/src/constants/json-ld/job-posting-json-ld.tsx +0 -92
  109. package/src/constants/json-ld/organization-json-ld.tsx +0 -62
  110. package/src/constants/json-ld/page-json-ld.tsx +0 -6
  111. package/src/constants/json-ld/product-json-ld.tsx +0 -154
  112. package/src/constants/json-ld/review-json-ld.tsx +0 -377
  113. package/src/constants/navigation-links/footer-links.ts +0 -48
  114. package/src/constants/navigation-links/nav-bar-links.ts +0 -41
  115. package/src/constants/navigation-links/navigation-link.ts +0 -6
  116. package/src/constants/pill-values.ts +0 -210
  117. package/src/constants/products.ts +0 -155
  118. package/src/constants/quiz-recommendations.ts +0 -506
  119. package/src/constants/reviews.ts +0 -75
  120. package/src/constants/staff.ts +0 -197
  121. package/src/constants/value-props.ts +0 -58
  122. package/src/data/dap-descriptions/session-couples-families-friends.en.json +0 -61
  123. package/src/data/dap-descriptions/session-elopements.en.json +0 -60
  124. package/src/data/dap-descriptions/session-proposals.en.json +0 -60
  125. package/src/data/product-descriptions/afternoon-delight.en.json +0 -35
  126. package/src/data/product-descriptions/emerald-lake-escape.en.json +0 -68
  127. package/src/data/product-descriptions/lake-louise-adventure.en.json +0 -74
  128. package/src/data/product-descriptions/moraine-lake-adventure.en.json +0 -78
  129. package/src/data/product-descriptions/moraine-lake-sunrise-lake-louise-golden-hour.en.json +0 -65
  130. package/src/data/product-descriptions/moraine-lake-sunrise.en.json +0 -64
  131. package/src/data/product-descriptions/private-tour.en.json +0 -80
  132. package/src/data/product-descriptions/two-lakes-combo.en.json +0 -65
  133. package/src/data/products-config.json +0 -101
  134. package/src/hooks/use-bottom-sheet.tsx +0 -15
  135. package/src/hooks/use-simple-modal.tsx +0 -27
  136. package/src/hooks/useBookingSourceMetadataFromLocation.ts +0 -21
  137. package/src/hooks/useEmailSubscription.tsx +0 -103
  138. package/src/hooks/useEmbeddedInIframe.ts +0 -16
  139. package/src/hooks/useIsBookingLaunchLive.ts +0 -49
  140. package/src/hooks/useQuiz.tsx +0 -210
  141. package/src/providers/bottom-sheet-provider.tsx +0 -40
  142. package/src/providers/dependent-add-on-dialog-provider.tsx +0 -105
  143. package/src/radius.css +0 -5
  144. package/src/spacing.css +0 -7
  145. package/src/strings/en.json +0 -1774
  146. package/src/strings/es.json +0 -1573
  147. package/src/strings/fr.json +0 -1573
  148. package/src/strings/index.js +0 -23
  149. package/src/text-style.css +0 -97
  150. package/src/types/fareharbor.d.ts +0 -12
  151. package/src/types/quiz.ts +0 -59
  152. package/src/utils/currency-converter.ts +0 -101
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ticketboothapp/booking",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "publishConfig": {
@@ -1,46 +1,302 @@
1
1
  'use client';
2
2
 
3
- import { useEffect } from 'react';
4
- import PartnerBookingPageWithBrowserMetadata from '@/components/partner/PartnerBookingPageWithBrowserMetadata';
5
- import type { BookingFlowUiOptions } from '@/components/booking/booking-flow-ui';
6
- import type { BookingSourceMetadata } from '@/lib/booking/source-metadata';
7
- import { setPartnerPortalBookingJwtGetter } from '@/lib/booking-api';
3
+ import { useState, useEffect, useCallback } from 'react';
4
+ import { CompanyProvider } from '@/contexts/CompanyContext';
5
+ import {
6
+ BookingAppProvider,
7
+ type BookingAppMode,
8
+ type BookingAppPermissions,
9
+ type ManageParams,
10
+ } from '@/contexts/BookingAppContext';
11
+ import { BookingFlow } from '@/components/booking/BookingFlow';
12
+ import { PrivateShuttleBookingFlow } from '@/components/booking/PrivateShuttleBookingFlow';
13
+ import { CurrencySwitcher, useCurrency, type Currency } from '@/components/booking/CurrencySwitcher';
14
+ import { ErrorBoundary } from '@/components/booking/ErrorBoundary';
15
+ import { getProduct, setPartnerPortalBookingJwtGetter, type Product } from '@/lib/booking-api';
16
+ import { getProductByIdOrSlug } from '@/lib/products-config';
17
+ import { ENV } from '@/lib/env';
18
+ import BookingProductGrid from '@/components/booking/BookingProductGrid';
19
+ import { BookingDialogProvider } from '@/providers/booking-dialog-provider';
20
+
21
+ type Step = 'products' | 'booking';
22
+
23
+ interface NavState {
24
+ step: Step;
25
+ selectedProduct: Product | null;
26
+ }
8
27
 
9
28
  export interface BookingWidgetProps {
29
+ initialProductId?: string;
30
+ initialCurrency?: Currency;
31
+ showHeader?: boolean;
10
32
  authToken?: string | null;
33
+ onProductSelect?: (product: Product) => void;
11
34
  onBookingSuccess?: (data: { reservationReference: string; sessionId?: string }) => void;
12
- flowUi?: BookingFlowUiOptions;
13
- bookingSourceAttribution?: Partial<BookingSourceMetadata>;
14
- bookOnTileClick?: boolean;
35
+ onBack?: () => void;
36
+ className?: string;
37
+ mode?: BookingAppMode;
38
+ permissions?: Partial<BookingAppPermissions>;
39
+ googleMapsApiKey?: string;
40
+ onShowManage?: (params: ManageParams) => void;
41
+ getSuccessUrl?: (params: { reservationRef: string; lastName: string; focusDate?: string }) => string;
42
+ showLanguageSelector?: boolean;
43
+ initialProduct?: Product;
44
+ products?: Product[];
45
+ companyId?: string;
46
+ initialBooking?: {
47
+ bookingReference: string;
48
+ productId: string;
49
+ availabilityId?: string;
50
+ dateTime: string;
51
+ originalTotalAmount?: number;
52
+ originalCurrency?: string;
53
+ bookingItems: Array<{ category: string; count: number }>;
54
+ returnAvailabilityId?: string | null;
55
+ pickupLocationId?: string | null;
56
+ travelerHotel?: string | null;
57
+ startTime?: string | null;
58
+ privateShuttleDetails?: { passengerCount?: number };
59
+ cancellationPolicyId?: string | null;
60
+ promoCode?: string | null;
61
+ additionalHoursCount?: number | null;
62
+ addOnSelections?: Array<{ addOnId: string; variantId?: string; quantity?: number }> | null;
63
+ };
64
+ /** Last name on the booking — required for public change-quote APIs inside the flow. */
65
+ changeFlowLastName?: string;
15
66
  }
16
67
 
68
+ /**
69
+ * Embeddable booking UI (product grid + flow). Used by provider-dashboard change-booking in “change mode”.
70
+ */
17
71
  export function BookingWidget({
72
+ initialProductId,
73
+ initialProduct,
74
+ initialBooking,
75
+ changeFlowLastName = '',
76
+ products: productsProp,
77
+ companyId: _companyId,
78
+ initialCurrency = 'CAD',
79
+ showHeader = true,
18
80
  authToken,
81
+ onProductSelect,
19
82
  onBookingSuccess,
20
- flowUi,
21
- bookingSourceAttribution,
22
- bookOnTileClick = true,
83
+ onBack,
84
+ className = '',
85
+ mode = 'standalone',
86
+ permissions = {},
87
+ googleMapsApiKey,
88
+ onShowManage,
89
+ getSuccessUrl,
90
+ showLanguageSelector = true,
23
91
  }: BookingWidgetProps) {
92
+ const { currency, setCurrency } = useCurrency();
93
+
24
94
  useEffect(() => {
25
95
  setPartnerPortalBookingJwtGetter(() => authToken ?? null);
26
96
  return () => setPartnerPortalBookingJwtGetter(() => null);
27
97
  }, [authToken]);
28
98
 
99
+ useEffect(() => {
100
+ if (initialCurrency) setCurrency(initialCurrency);
101
+ // eslint-disable-next-line react-hooks/exhaustive-deps
102
+ }, []);
103
+
104
+ const isChangeMode = !!(initialProduct && initialBooking);
105
+
106
+ const flowInitialValues = initialBooking
107
+ ? {
108
+ bookingReference: initialBooking.bookingReference,
109
+ dateTime: initialBooking.dateTime,
110
+ availabilityId: initialBooking.availabilityId ?? null,
111
+ productOptionId: initialBooking.productId,
112
+ pickupLocationId: initialBooking.pickupLocationId ?? null,
113
+ returnAvailabilityId: initialBooking.returnAvailabilityId ?? null,
114
+ bookingItems: initialBooking.bookingItems,
115
+ addOnSelections: initialBooking.addOnSelections ?? null,
116
+ promoCode: initialBooking.promoCode ?? null,
117
+ cancellationPolicyId: initialBooking.cancellationPolicyId ?? null,
118
+ customer: changeFlowLastName ? { lastName: changeFlowLastName } : null,
119
+ }
120
+ : undefined;
121
+
122
+ const privateShuttleInitialValues = initialBooking
123
+ ? {
124
+ bookingReference: initialBooking.bookingReference,
125
+ dateTime: initialBooking.dateTime,
126
+ pickupLocationId: initialBooking.pickupLocationId ?? null,
127
+ customPickupAddress: initialBooking.travelerHotel ?? null,
128
+ passengers: initialBooking.privateShuttleDetails?.passengerCount ?? null,
129
+ additionalHoursCount: initialBooking.additionalHoursCount ?? null,
130
+ notes: null,
131
+ specialRequest: null,
132
+ }
133
+ : undefined;
134
+
135
+ const bookingSourceAttribution: import('@/lib/booking/source-metadata').BookingSourceMetadata = {
136
+ pagePath: '/provider-dashboard',
137
+ };
138
+
139
+ const [navState, setNavState] = useState<NavState>(() => {
140
+ if (isChangeMode && initialProduct) {
141
+ return { step: 'booking', selectedProduct: initialProduct };
142
+ }
143
+ return { step: 'products', selectedProduct: null };
144
+ });
145
+
146
+ const [error, setError] = useState('');
147
+
148
+ useEffect(() => {
149
+ if (!isChangeMode || !productsProp?.length || !initialProductId) return;
150
+ const product = productsProp.find(
151
+ (p) => p.productId === initialProductId || p.options?.some((o) => o.optionId === initialProductId)
152
+ );
153
+ if (product) setNavState({ step: 'booking', selectedProduct: product });
154
+ }, [isChangeMode, initialProductId, productsProp]);
155
+
156
+ useEffect(() => {
157
+ if (isChangeMode || !initialProductId) return;
158
+ let cancelled = false;
159
+ (async () => {
160
+ const config = getProductByIdOrSlug(initialProductId);
161
+ const apiProductId = config?.productId ?? initialProductId;
162
+ try {
163
+ const p = await getProduct(apiProductId, ENV.COMPANY_ID);
164
+ if (!cancelled && p) setNavState({ step: 'booking', selectedProduct: p });
165
+ } catch (e) {
166
+ if (!cancelled) setError(e instanceof Error ? e.message : 'Failed to load product');
167
+ }
168
+ })();
169
+ return () => {
170
+ cancelled = true;
171
+ };
172
+ }, [isChangeMode, initialProductId]);
173
+
174
+ const handleSelectProduct = useCallback(
175
+ (product: Product) => {
176
+ setNavState({ step: 'booking', selectedProduct: product });
177
+ onProductSelect?.(product);
178
+ },
179
+ [onProductSelect]
180
+ );
181
+
182
+ const handleBookProductId = useCallback(
183
+ async (productSlugOrId: string) => {
184
+ const config = getProductByIdOrSlug(productSlugOrId);
185
+ const apiProductId = config?.productId ?? productSlugOrId;
186
+ try {
187
+ const p = await getProduct(apiProductId, ENV.COMPANY_ID);
188
+ if (p) handleSelectProduct(p);
189
+ } catch (e) {
190
+ setError(e instanceof Error ? e.message : 'Failed to load product');
191
+ }
192
+ },
193
+ [handleSelectProduct]
194
+ );
195
+
196
+ const handleBack = useCallback(() => {
197
+ setNavState({ step: 'products', selectedProduct: null });
198
+ onBack?.();
199
+ }, [onBack]);
200
+
201
+ const handleBookingSuccess = useCallback(
202
+ (data: { reservationReference: string; sessionId?: string }) => {
203
+ onBookingSuccess?.(data);
204
+ },
205
+ [onBookingSuccess]
206
+ );
207
+
29
208
  return (
30
- <PartnerBookingPageWithBrowserMetadata
31
- embedded
32
- initialFilterId="all"
33
- hideProductGridHeader
34
- bookOnTileClick={bookOnTileClick}
35
- flowUi={flowUi}
36
- canonicalAttribution={{ pagePath: '/provider-dashboard' }}
37
- bookingSourceAttributionMerge={bookingSourceAttribution}
38
- onBookingFlowSuccess={(data) => {
39
- onBookingSuccess?.({
40
- reservationReference: data.reservationReference,
41
- sessionId: data.bookingReference,
42
- });
43
- }}
44
- />
209
+ <CompanyProvider>
210
+ <BookingAppProvider
211
+ mode={mode}
212
+ permissions={permissions}
213
+ googleMapsApiKey={googleMapsApiKey}
214
+ onShowManage={onShowManage}
215
+ getSuccessUrl={getSuccessUrl}
216
+ showLanguageSelector={showLanguageSelector}
217
+ >
218
+ <BookingDialogProvider>
219
+ <div className={`min-h-0 overflow-x-hidden ${className}`}>
220
+ {showHeader && (
221
+ <header className="py-4 w-full overflow-hidden" style={{ backgroundColor: 'var(--booking-header-bg)', color: 'var(--booking-header-text)' }}>
222
+ <div className="max-w-4xl mx-auto px-4 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 min-w-0">
223
+ <h1 className="text-xl font-semibold shrink-0" style={{ fontFamily: 'var(--booking-font-sans)' }}>
224
+ Via Via Moraine Lake
225
+ </h1>
226
+ <div className="flex flex-wrap items-center gap-2 sm:gap-4 min-w-0">
227
+ <CurrencySwitcher currency={currency} onCurrencyChange={setCurrency} />
228
+ </div>
229
+ </div>
230
+ </header>
231
+ )}
232
+
233
+ {!showHeader && (
234
+ <div
235
+ className="border-b px-4 py-2 flex flex-wrap items-center justify-end gap-2 min-w-0"
236
+ style={{ borderColor: 'var(--booking-border)', backgroundColor: 'var(--booking-surface)' }}
237
+ >
238
+ <CurrencySwitcher currency={currency} onCurrencyChange={setCurrency} />
239
+ </div>
240
+ )}
241
+
242
+ <main className={`max-w-4xl mx-auto px-4 py-8 ${!showHeader ? 'pt-4' : ''}`} style={{ fontFamily: 'var(--booking-font-sans)' }}>
243
+ <div
244
+ className="overflow-visible p-8"
245
+ style={{
246
+ backgroundColor: 'var(--booking-surface)',
247
+ borderRadius: 'var(--booking-radius)',
248
+ boxShadow: '0 25px 50px -12px rgba(0,0,0,0.25)',
249
+ }}
250
+ >
251
+ {error ? (
252
+ <div
253
+ className="p-4 rounded-lg"
254
+ style={{
255
+ backgroundColor: 'var(--booking-error-bg)',
256
+ borderWidth: '1px',
257
+ borderStyle: 'solid',
258
+ borderColor: 'var(--booking-error-border)',
259
+ color: 'var(--booking-error-text)',
260
+ }}
261
+ >
262
+ {error}
263
+ </div>
264
+ ) : navState.step === 'products' ? (
265
+ <BookingProductGrid
266
+ initialFilterId="all"
267
+ bookOnTileClick
268
+ onBookProduct={handleBookProductId}
269
+ />
270
+ ) : navState.selectedProduct ? (
271
+ <ErrorBoundary>
272
+ {navState.selectedProduct.productType === 'PRIVATE_SHUTTLE' ? (
273
+ <PrivateShuttleBookingFlow
274
+ product={navState.selectedProduct}
275
+ onBack={handleBack}
276
+ currency={currency}
277
+ onSuccess={handleBookingSuccess}
278
+ mode={isChangeMode ? 'change' : 'standard'}
279
+ initialValues={isChangeMode ? privateShuttleInitialValues : undefined}
280
+ bookingSourceAttribution={bookingSourceAttribution}
281
+ />
282
+ ) : (
283
+ <BookingFlow
284
+ product={navState.selectedProduct}
285
+ onBack={handleBack}
286
+ currency={currency}
287
+ onSuccess={handleBookingSuccess}
288
+ mode={isChangeMode ? 'change' : 'standard'}
289
+ initialValues={isChangeMode ? flowInitialValues : undefined}
290
+ bookingSourceAttribution={bookingSourceAttribution}
291
+ />
292
+ )}
293
+ </ErrorBoundary>
294
+ ) : null}
295
+ </div>
296
+ </main>
297
+ </div>
298
+ </BookingDialogProvider>
299
+ </BookingAppProvider>
300
+ </CompanyProvider>
45
301
  );
46
302
  }
@@ -2,13 +2,13 @@
2
2
 
3
3
  import { useState, useEffect, useLayoutEffect } from 'react';
4
4
  import { formatBookingRefForDisplay } from '@/lib/booking-ref';
5
- import { BookingDetails, type BookingData } from '@/components/BookingDetails';
6
- import { type Currency } from '@/components/CurrencySwitcher';
5
+ import BookingDetails, { type BookingData } from '@/components/BookingDetails';
6
+ import { type Currency, formatCurrencyAmount } from '@/lib/currency';
7
7
  import { ENV } from '@/lib/env';
8
- import { formatCurrencyAmount } from '@/lib/currency';
9
8
 
10
9
  const API_URL = ENV.API_URL;
11
10
 
11
+ /** Provider-only fields merged after public manage lookup (e.g. embedded in provider dashboard). */
12
12
  export interface StaffBookingAttribution {
13
13
  reportingSource: string;
14
14
  partnerBookingPortal: boolean;
@@ -121,12 +121,14 @@ export function ManageBookingView({
121
121
  if (cancelled) return;
122
122
  setBooking(b);
123
123
 
124
- const hasPaymentOwing = (bookingData: BookingData) => {
125
- const status = bookingData?.payment?.status;
126
- const balanceAmount = bookingData?.payment?.plan?.balanceAmount ?? 0;
127
- const depositAmount = bookingData?.payment?.plan?.depositAmount ?? 0;
128
- return (status === 'DEPOSIT_PAID' && balanceAmount > 0) ||
129
- (status === 'AWAITING_PAYMENT' && (depositAmount > 0 || balanceAmount > 0));
124
+ const hasPaymentOwing = (bk: BookingData) => {
125
+ const status = bk?.payment?.status;
126
+ const balanceAmount = bk?.payment?.plan?.balanceAmount ?? 0;
127
+ const depositAmount = bk?.payment?.plan?.depositAmount ?? 0;
128
+ return (
129
+ (status === 'DEPOSIT_PAID' && balanceAmount > 0) ||
130
+ (status === 'AWAITING_PAYMENT' && (depositAmount > 0 || balanceAmount > 0))
131
+ );
130
132
  };
131
133
 
132
134
  if (initialPaymentSuccess && b && hasPaymentOwing(b)) {
@@ -147,7 +149,9 @@ export function ManageBookingView({
147
149
  }
148
150
  };
149
151
  doLookup();
150
- return () => { cancelled = true; };
152
+ return () => {
153
+ cancelled = true;
154
+ };
151
155
  }, [initialRef, initialLastName, initialPaymentSuccess, replaceUrl]);
152
156
 
153
157
  useEffect(() => {
@@ -180,7 +184,9 @@ export function ManageBookingView({
180
184
  return;
181
185
  }
182
186
  }
183
- } catch { /* ignore */ }
187
+ } catch {
188
+ /* ignore */
189
+ }
184
190
  if (cancelled) return;
185
191
  attempt += 1;
186
192
  if (attempt * POLL_MS < MAX_MS) {
@@ -193,7 +199,9 @@ export function ManageBookingView({
193
199
  await poll();
194
200
  };
195
201
  doLookup();
196
- return () => { cancelled = true; };
202
+ return () => {
203
+ cancelled = true;
204
+ };
197
205
  }, [initialReservationRef, initialLastName, initialRef, replaceUrl]);
198
206
 
199
207
  async function handleLookup() {
@@ -239,7 +247,7 @@ export function ManageBookingView({
239
247
  setBooking(data.data);
240
248
  }
241
249
  } catch {
242
- // ignore
250
+ /* ignore */
243
251
  }
244
252
  }
245
253
 
@@ -264,7 +272,11 @@ export function ManageBookingView({
264
272
  </div>
265
273
  )}
266
274
  {staffAttribution ? (
267
- <div className="mb-4 rounded-xl border border-stone-200 bg-stone-50 px-4 py-3 text-sm text-stone-800" role="region" aria-label="Staff booking attribution">
275
+ <div
276
+ className="mb-4 rounded-xl border border-stone-200 bg-stone-50 px-4 py-3 text-sm text-stone-800"
277
+ role="region"
278
+ aria-label="Staff booking attribution"
279
+ >
268
280
  <p className="font-semibold text-stone-900 mb-2">Booking attribution</p>
269
281
  <p>
270
282
  <span className="text-stone-600">Channel:</span> {staffChannelLabel(staffAttribution.reportingSource)}
@@ -272,7 +284,9 @@ export function ManageBookingView({
272
284
  {staffAttribution.partnerId ? (
273
285
  <p>
274
286
  <span className="text-stone-600">Partner:</span>{' '}
275
- {staffAttribution.partnerName ? `${staffAttribution.partnerName} (${staffAttribution.partnerId})` : staffAttribution.partnerId}
287
+ {staffAttribution.partnerName
288
+ ? `${staffAttribution.partnerName} (${staffAttribution.partnerId})`
289
+ : staffAttribution.partnerId}
276
290
  </p>
277
291
  ) : null}
278
292
  {staffAttribution.agentDisplay || staffAttribution.agentId ? (
@@ -308,7 +322,11 @@ export function ManageBookingView({
308
322
  if (waitingForBooking && loading) {
309
323
  return (
310
324
  <div className={containerClass}>
311
- <div className="w-10 h-10 border-2 border-t-transparent rounded-full animate-spin" style={{ borderColor: 'var(--booking-primary)' }} aria-hidden />
325
+ <div
326
+ className="w-10 h-10 border-2 border-t-transparent rounded-full animate-spin"
327
+ style={{ borderColor: 'var(--booking-primary)' }}
328
+ aria-hidden
329
+ />
312
330
  <p style={{ color: 'var(--booking-text-muted)' }}>Confirming your booking…</p>
313
331
  </div>
314
332
  );
@@ -316,10 +334,15 @@ export function ManageBookingView({
316
334
 
317
335
  return (
318
336
  <div className={formOuterClass || undefined}>
319
- <div className="p-8 max-w-md w-full shadow-xl" style={{ backgroundColor: 'var(--booking-surface)', borderRadius: 'var(--booking-radius)' }}>
337
+ <div
338
+ className="p-8 max-w-md w-full shadow-xl"
339
+ style={{ backgroundColor: 'var(--booking-surface)', borderRadius: 'var(--booking-radius)' }}
340
+ >
320
341
  {isEmbed && (
321
342
  <div className={`flex justify-between items-center mb-4 ${showClose ? '' : 'justify-start'}`}>
322
- <h1 className="text-2xl font-bold" style={{ color: 'var(--booking-text)' }}>Manage Your Booking</h1>
343
+ <h1 className="text-2xl font-bold" style={{ color: 'var(--booking-text)' }}>
344
+ Manage Your Booking
345
+ </h1>
323
346
  {showClose && (
324
347
  <button type="button" onClick={onClose} className="text-sm hover:opacity-80" style={{ color: 'var(--booking-text-muted)' }}>
325
348
  Close
@@ -327,9 +350,19 @@ export function ManageBookingView({
327
350
  )}
328
351
  </div>
329
352
  )}
330
- {!isEmbed && <h1 className="text-2xl font-bold mb-6" style={{ color: 'var(--booking-text)' }}>Manage Your Booking</h1>}
353
+ {!isEmbed && (
354
+ <h1 className="text-2xl font-bold mb-6" style={{ color: 'var(--booking-text)' }}>
355
+ Manage Your Booking
356
+ </h1>
357
+ )}
331
358
 
332
- <form onSubmit={(e) => { e.preventDefault(); handleLookup(); }} className="space-y-4">
359
+ <form
360
+ onSubmit={(e) => {
361
+ e.preventDefault();
362
+ handleLookup();
363
+ }}
364
+ className="space-y-4"
365
+ >
333
366
  <div>
334
367
  <label htmlFor="manage-booking-ref" className="block text-sm font-medium mb-1" style={{ color: 'var(--booking-text)' }}>
335
368
  Booking Reference
@@ -361,7 +394,16 @@ export function ManageBookingView({
361
394
  />
362
395
  </div>
363
396
  {error && (
364
- <div className="rounded-lg p-3 text-sm" style={{ backgroundColor: 'var(--booking-error-bg)', border: '1px solid var(--booking-error-border)', color: 'var(--booking-error-text)' }}>{error}</div>
397
+ <div
398
+ className="rounded-lg p-3 text-sm"
399
+ style={{
400
+ backgroundColor: 'var(--booking-error-bg)',
401
+ border: '1px solid var(--booking-error-border)',
402
+ color: 'var(--booking-error-text)',
403
+ }}
404
+ >
405
+ {error}
406
+ </div>
365
407
  )}
366
408
  <button
367
409
  type="submit"
@@ -374,9 +416,19 @@ export function ManageBookingView({
374
416
  </form>
375
417
 
376
418
  {!isEmbed && (
377
- <div className="mt-6 pt-6 text-center text-sm" style={{ borderColor: 'var(--booking-border)', borderTopWidth: '1px', borderTopStyle: 'solid', color: 'var(--booking-text-muted)' }}>
419
+ <div
420
+ className="mt-6 pt-6 text-center text-sm"
421
+ style={{
422
+ borderColor: 'var(--booking-border)',
423
+ borderTopWidth: '1px',
424
+ borderTopStyle: 'solid',
425
+ color: 'var(--booking-text-muted)',
426
+ }}
427
+ >
378
428
  <p>Enter your booking reference and last name to view your booking details.</p>
379
- <p className="mt-1" style={{ color: 'var(--booking-text-muted)' }}>You can also share a link: /manage?ref=ABC12345&amp;lastName=Smith</p>
429
+ <p className="mt-1" style={{ color: 'var(--booking-text-muted)' }}>
430
+ You can also share a link: /manage?ref=ABC12345&amp;lastName=Smith
431
+ </p>
380
432
  </div>
381
433
  )}
382
434
  </div>
@@ -19,7 +19,7 @@ import defaultStrings from '@/strings';
19
19
  import { getImageUrl } from '@/constants/images';
20
20
  import BackgroundPlayer from 'next-video/background-player';
21
21
  import styles from './BookingProductGrid.module.css';
22
- import Button, { ButtonHoverColor } from '../button';
22
+ import Button, { ButtonHoverColor } from '@/components/button';
23
23
 
24
24
  const DEFAULT_VIDEO = {
25
25
  src: '/videos/via-via-moraine-lake-tour-video.mp4',
@@ -381,17 +381,17 @@
381
381
  flex-direction: column;
382
382
  align-items: center;
383
383
  justify-content: center;
384
- min-height: 6.25rem; /* Desktop: taller base cell for availability-mode readability */
384
+ min-height: 5.5rem; /* Desktop: enough room for time pills */
385
385
  cursor: pointer;
386
386
  }
387
387
 
388
388
  /* Admin (showCapacity): extra line under each time pill — taller cells on sm+ only */
389
389
  @media (min-width: 640px) {
390
390
  .calendar .calendarDayCell.calendarDayCellWithAdminCapacity {
391
- min-height: 8.25rem;
391
+ min-height: 7.5rem;
392
392
  }
393
393
  .calendar .calendarDayCell.calendarDayCellWithAdminCapacity.calendarDayCellWithAdminCapacityTall {
394
- min-height: 9.75rem;
394
+ min-height: 9rem;
395
395
  }
396
396
  }
397
397
 
@@ -6,7 +6,7 @@ import { PickupLocationSelector } from './PickupLocationSelector';
6
6
  import type { Currency } from './CurrencySwitcher';
7
7
  import type { PickupLocation, Destination } from '@/lib/booking-api';
8
8
  import styles from './CheckoutForm.module.css';
9
- import Button, { ButtonHoverColor } from '../button';
9
+ import Button, { ButtonHoverColor } from '@/components/button';
10
10
  import { AnimatePresence, motion } from 'framer-motion';
11
11
 
12
12
  type TranslationFn = (key: string, params?: Record<string, string>) => string;
@@ -890,7 +890,7 @@ function PickupLocationSelectorWithMap(props: PickupLocationSelectorProps) {
890
890
 
891
891
  {/* Input with search icon inside - padding ensures text stays to the right of icon */}
892
892
  <div className="relative">
893
- <div className="absolute left-0 top-1/2 -translate-y-1/2 pl-4 flex items-center pointer-events-none text-stone-400">
893
+ <div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none text-stone-400">
894
894
  <svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
895
895
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
896
896
  </svg>
package/src/index.ts CHANGED
@@ -31,9 +31,10 @@ export {
31
31
  CompanyProvider,
32
32
  useCompany,
33
33
  useCompanyTimezone,
34
- type Company,
35
34
  } from './contexts/CompanyContext';
36
35
 
36
+ export { type Company } from './lib/booking-api';
37
+
37
38
  export {
38
39
  AVAILABILITIES_CACHE_TTL_MS,
39
40
  AvailabilitiesCacheProvider,
@@ -55,6 +56,7 @@ export {
55
56
 
56
57
  export {
57
58
  setPartnerPortalBookingJwtGetter,
59
+ type Product,
58
60
  } from './lib/booking-api';
59
61
 
60
62
  export {