@ticketboothapp/booking 0.1.11 → 0.1.12

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 (252) hide show
  1. package/package.json +1 -1
  2. package/src/colours.css +23 -0
  3. package/src/components/BookingDetails.module.css +1591 -0
  4. package/src/components/BookingDetails.tsx +2072 -354
  5. package/src/components/BookingWidget.tsx +28 -248
  6. package/src/components/JobApplicationDialog.module.css +440 -0
  7. package/src/components/JobApplicationDialog.tsx +620 -0
  8. package/src/components/ManageBookingView.tsx +28 -36
  9. package/src/components/PhoneInputWithCountry.module.css +131 -0
  10. package/src/components/PhoneInputWithCountry.tsx +44 -0
  11. package/src/components/PickupLocationDialog.module.css +360 -0
  12. package/src/components/PickupLocationDialog.tsx +357 -0
  13. package/src/components/PickupLocationMap.tsx +110 -0
  14. package/src/components/PostBookingDependentAddOnUpsell.module.css +174 -0
  15. package/src/components/PostBookingDependentAddOnUpsell.tsx +407 -0
  16. package/src/components/accordion.css +27 -0
  17. package/src/components/accordion.tsx +29 -0
  18. package/src/components/analytics/AnalyticsConsentRestore.tsx +19 -0
  19. package/src/components/analytics/AnalyticsScripts.tsx +106 -0
  20. package/src/components/analytics/CookieConsentBanner.css +86 -0
  21. package/src/components/analytics/CookieConsentBanner.tsx +102 -0
  22. package/src/components/booking/AddOnsSection.module.css +10 -0
  23. package/src/components/booking/AddOnsSection.tsx +184 -0
  24. package/src/components/booking/AdminPaymentChoiceModal.tsx +98 -0
  25. package/src/components/booking/BookingDialog.module.css +643 -0
  26. package/src/components/booking/BookingDialog.tsx +356 -0
  27. package/src/components/booking/BookingFlow.tsx +4385 -0
  28. package/src/components/booking/BookingFlowCollage.module.css +148 -0
  29. package/src/components/booking/BookingFlowCollage.tsx +184 -0
  30. package/src/components/booking/BookingFlowPlaceholder.module.css +27 -0
  31. package/src/components/booking/BookingFlowPlaceholder.tsx +25 -0
  32. package/src/components/booking/BookingFlowPreview.tsx +51 -0
  33. package/src/components/booking/BookingProductGrid.module.css +359 -0
  34. package/src/components/booking/BookingProductGrid.tsx +497 -0
  35. package/src/components/booking/Calendar.module.css +616 -0
  36. package/src/components/{Calendar.tsx → booking/Calendar.tsx} +464 -247
  37. package/src/components/booking/CancellationPolicySelector.module.css +124 -0
  38. package/src/components/booking/CancellationPolicySelector.tsx +142 -0
  39. package/src/components/booking/ChangeBookingDialog.tsx +562 -0
  40. package/src/components/booking/CheckoutForm.module.css +244 -0
  41. package/src/components/booking/CheckoutForm.tsx +364 -0
  42. package/src/components/{CheckoutModal.tsx → booking/CheckoutModal.tsx} +176 -19
  43. package/src/components/booking/DapFlowCollage.tsx +88 -0
  44. package/src/components/booking/DapTourDescription.tsx +35 -0
  45. package/src/components/booking/DependentAddOnBookingDialog.tsx +1350 -0
  46. package/src/components/booking/DependentAddOnPaymentForm.tsx +124 -0
  47. package/src/components/booking/InfoTooltip.tsx +108 -0
  48. package/src/components/booking/ItineraryBox.module.css +258 -0
  49. package/src/components/booking/ItineraryBox.tsx +550 -0
  50. package/src/components/{ItineraryBuilder.tsx → booking/ItineraryBuilder.tsx} +1 -2
  51. package/src/components/booking/ItineraryPlaceholder.module.css +45 -0
  52. package/src/components/booking/ItineraryPlaceholder.tsx +26 -0
  53. package/src/components/{MealDrinkAddOnSelector.tsx → booking/MealDrinkAddOnSelector.tsx} +21 -13
  54. package/src/components/booking/PickupLocationSelector.module.css +124 -0
  55. package/src/components/{PickupLocationSelector.tsx → booking/PickupLocationSelector.tsx} +315 -290
  56. package/src/components/booking/PickupTimeSelector.module.css +134 -0
  57. package/src/components/booking/PickupTimeSelector.tsx +112 -0
  58. package/src/components/{PriceBreakdown.tsx → booking/PriceBreakdown.tsx} +3 -3
  59. package/src/components/{PriceSummary.tsx → booking/PriceSummary.tsx} +51 -28
  60. package/src/components/booking/PrivateShuttleBookingFlow.module.css +357 -0
  61. package/src/components/booking/PrivateShuttleBookingFlow.tsx +2662 -0
  62. package/src/components/booking/PromoCodeInput.module.css +166 -0
  63. package/src/components/booking/PromoCodeInput.tsx +99 -0
  64. package/src/components/booking/ReturnTimeSelector.module.css +173 -0
  65. package/src/components/booking/ReturnTimeSelector.tsx +145 -0
  66. package/src/components/{TermsAcceptance.tsx → booking/TermsAcceptance.tsx} +9 -8
  67. package/src/components/booking/TicketSelector.module.css +164 -0
  68. package/src/components/booking/TicketSelector.tsx +199 -0
  69. package/src/components/booking/TourDescription.module.css +304 -0
  70. package/src/components/booking/TourDescription.tsx +273 -0
  71. package/src/components/booking/booking-flow-ui.ts +15 -1
  72. package/src/components/booking/booking-flow.css +944 -0
  73. package/src/components/bottom-sheet.module.css +78 -0
  74. package/src/components/bottom-sheet.tsx +60 -0
  75. package/src/components/breadcrumb.module.css +40 -0
  76. package/src/components/breadcrumb.tsx +36 -0
  77. package/src/components/button.css +245 -0
  78. package/src/components/button.tsx +152 -0
  79. package/src/components/client-bottom-sheet.tsx +14 -0
  80. package/src/components/colorable-svg.tsx +29 -0
  81. package/src/components/conditional-footer.tsx +27 -0
  82. package/src/components/contact-us.module.css +147 -0
  83. package/src/components/contact-us.tsx +49 -0
  84. package/src/components/email-signup.css +151 -0
  85. package/src/components/email-signup.tsx +63 -0
  86. package/src/components/faq-wrapper.module.css +47 -0
  87. package/src/components/faq-wrapper.tsx +15 -0
  88. package/src/components/footer.css +187 -0
  89. package/src/components/footer.tsx +143 -0
  90. package/src/components/global-simple-modal.tsx +33 -0
  91. package/src/components/google-review-summary.module.css +77 -0
  92. package/src/components/google-review-summary.tsx +50 -0
  93. package/src/components/hero-image.css +13 -0
  94. package/src/components/hero-image.tsx +44 -0
  95. package/src/components/image.css +29 -0
  96. package/src/components/image.tsx +113 -0
  97. package/src/components/language-aware-link.tsx +72 -0
  98. package/src/components/language-switcher.module.css +124 -0
  99. package/src/components/language-switcher.tsx +75 -0
  100. package/src/components/map-section.css +59 -0
  101. package/src/components/map-section.tsx +63 -0
  102. package/src/components/navbar.module.css +152 -0
  103. package/src/components/navbar.tsx +125 -0
  104. package/src/components/parallax-provider.tsx +11 -0
  105. package/src/components/partner/PartnerBookingPage.module.css +130 -0
  106. package/src/components/partner/PartnerBookingPage.tsx +390 -0
  107. package/src/components/partner/PartnerBookingPageWithBrowserMetadata.tsx +19 -35
  108. package/src/components/product-tag.module.css +30 -0
  109. package/src/components/product-tag.tsx +34 -0
  110. package/src/components/product-theme-pages/best-option.module.css +70 -0
  111. package/src/components/product-theme-pages/best-option.tsx +35 -0
  112. package/src/components/product-theme-pages/extended-tour-options.module.css +22 -0
  113. package/src/components/product-theme-pages/extended-tour-options.tsx +11 -0
  114. package/src/components/product-theme-pages/image-modal.tsx +248 -0
  115. package/src/components/product-theme-pages/photo-gallery.module.css +200 -0
  116. package/src/components/product-theme-pages/photo-gallery.tsx +90 -0
  117. package/src/components/product-theme-pages/product-theme-page-layout.module.css +13 -0
  118. package/src/components/product-theme-pages/product-theme-page-layout.tsx +67 -0
  119. package/src/components/product-theme-pages/top-of-fold.module.css +179 -0
  120. package/src/components/product-theme-pages/top-of-fold.tsx +80 -0
  121. package/src/components/product-tile/image-only-product-tile-desktop.module.css +106 -0
  122. package/src/components/product-tile/image-only-product-tile-desktop.tsx +56 -0
  123. package/src/components/product-tile/image-only-product-tile-mobile.module.css +122 -0
  124. package/src/components/product-tile/image-only-product-tile-mobile.tsx +89 -0
  125. package/src/components/product-tile/image-only-product-tile.tsx +44 -0
  126. package/src/components/product-tile/product-tile-card.module.css +84 -0
  127. package/src/components/product-tile/product-tile-card.tsx +61 -0
  128. package/src/components/review-highlights-section.css +85 -0
  129. package/src/components/review-highlights-section.tsx +127 -0
  130. package/src/components/season-closure-overlay.module.css +99 -0
  131. package/src/components/season-closure-overlay.tsx +98 -0
  132. package/src/components/simple-modal.tsx +69 -0
  133. package/src/components/simple-top-of-fold.module.css +76 -0
  134. package/src/components/simple-top-of-fold.tsx +34 -0
  135. package/src/components/spacer.css +41 -0
  136. package/src/components/spacer.tsx +23 -0
  137. package/src/components/star-rating.module.css +74 -0
  138. package/src/components/star-rating.tsx +48 -0
  139. package/src/components/terms/TermsContent.tsx +178 -0
  140. package/src/components/title-subtitle.module.css +10 -0
  141. package/src/components/title-subtitle.tsx +30 -0
  142. package/src/components/translatable-reviews.tsx +75 -0
  143. package/src/components/value-pill.module.css +59 -0
  144. package/src/components/value-pill.tsx +46 -0
  145. package/src/components/value-props.css +185 -0
  146. package/src/components/value-props.tsx +88 -0
  147. package/src/constants/booking-guide-quiz.ts +64 -0
  148. package/src/constants/contact-info.ts +2 -0
  149. package/src/constants/faq.ts +44 -0
  150. package/src/constants/images.ts +556 -0
  151. package/src/constants/json-ld/faq-json-ld.tsx +170 -0
  152. package/src/constants/json-ld/homepage-json-ld.tsx +138 -0
  153. package/src/constants/json-ld/job-posting-json-ld.tsx +92 -0
  154. package/src/constants/json-ld/organization-json-ld.tsx +62 -0
  155. package/src/constants/json-ld/page-json-ld.tsx +6 -0
  156. package/src/constants/json-ld/product-json-ld.tsx +154 -0
  157. package/src/constants/json-ld/review-json-ld.tsx +377 -0
  158. package/src/constants/navigation-links/footer-links.ts +48 -0
  159. package/src/constants/navigation-links/nav-bar-links.ts +41 -0
  160. package/src/constants/navigation-links/navigation-link.ts +6 -0
  161. package/src/constants/pill-values.ts +210 -0
  162. package/src/constants/products.ts +155 -0
  163. package/src/constants/quiz-recommendations.ts +506 -0
  164. package/src/constants/reviews.ts +75 -0
  165. package/src/constants/staff.ts +197 -0
  166. package/src/constants/value-props.ts +58 -0
  167. package/src/data/dap-descriptions/session-couples-families-friends.en.json +61 -0
  168. package/src/data/dap-descriptions/session-elopements.en.json +60 -0
  169. package/src/data/dap-descriptions/session-proposals.en.json +60 -0
  170. package/src/data/product-descriptions/afternoon-delight.en.json +35 -0
  171. package/src/data/product-descriptions/emerald-lake-escape.en.json +68 -0
  172. package/src/data/product-descriptions/lake-louise-adventure.en.json +74 -0
  173. package/src/data/product-descriptions/moraine-lake-adventure.en.json +78 -0
  174. package/src/data/product-descriptions/moraine-lake-sunrise-lake-louise-golden-hour.en.json +65 -0
  175. package/src/data/product-descriptions/moraine-lake-sunrise.en.json +64 -0
  176. package/src/data/product-descriptions/private-tour.en.json +80 -0
  177. package/src/data/product-descriptions/two-lakes-combo.en.json +65 -0
  178. package/src/data/products-config.json +101 -0
  179. package/src/hooks/use-bottom-sheet.tsx +15 -0
  180. package/src/hooks/use-simple-modal.tsx +27 -0
  181. package/src/hooks/useBookingSourceMetadataFromLocation.ts +21 -0
  182. package/src/hooks/useEmailSubscription.tsx +103 -0
  183. package/src/hooks/useEmbeddedInIframe.ts +16 -0
  184. package/src/hooks/useIsBookingLaunchLive.ts +49 -0
  185. package/src/hooks/useQuiz.tsx +210 -0
  186. package/src/index.ts +27 -2
  187. package/src/lib/analytics.ts +197 -0
  188. package/src/lib/booking/booking-source.ts +20 -2
  189. package/src/lib/{checkout-breakdown.ts → booking/checkout-breakdown.ts} +1 -1
  190. package/src/lib/booking/correlation-id.ts +46 -0
  191. package/src/lib/{i18n → booking/i18n}/messages/en.json +48 -4
  192. package/src/lib/{i18n → booking/i18n}/messages/fr.json +48 -4
  193. package/src/lib/booking/itinerary-display.ts +36 -0
  194. package/src/lib/{itinerary-labels.ts → booking/itinerary-labels.ts} +1 -1
  195. package/src/lib/{location-calculations.ts → booking/location-calculations.ts} +4 -4
  196. package/src/lib/{location-utils.ts → booking/location-utils.ts} +26 -0
  197. package/src/lib/{map-utils.ts → booking/map-utils.ts} +3 -3
  198. package/src/lib/booking/normalize-booking-product-id.ts +7 -0
  199. package/src/lib/{pickup-location-types.ts → booking/pickup-location-types.ts} +2 -2
  200. package/src/lib/{pricing.ts → booking/pricing.ts} +2 -2
  201. package/src/lib/booking/product-option-id.ts +35 -0
  202. package/src/lib/booking/source-metadata.ts +72 -7
  203. package/src/lib/booking/sunday-week.ts +14 -0
  204. package/src/lib/booking/trace-context.ts +62 -0
  205. package/src/lib/booking-api.ts +1793 -0
  206. package/src/lib/{constants.ts → booking-constants.ts} +11 -5
  207. package/src/lib/booking-types.ts +36 -0
  208. package/src/lib/currency.ts +38 -45
  209. package/src/lib/dap-descriptions.ts +50 -0
  210. package/src/lib/dap-itinerary-preview.ts +315 -0
  211. package/src/lib/dependent-add-on-api.ts +434 -0
  212. package/src/lib/env.ts +89 -5
  213. package/src/lib/firebase.ts +20 -0
  214. package/src/lib/job-application-api.ts +83 -0
  215. package/src/lib/manage-booking-embed-print.ts +16 -0
  216. package/src/lib/manage-booking-post-checkout.ts +68 -0
  217. package/src/lib/photo-dap-config.ts +228 -0
  218. package/src/lib/pickup/map-utils.ts +56 -0
  219. package/src/lib/pickup/marker-icons.ts +19 -0
  220. package/src/lib/product-descriptions.ts +66 -0
  221. package/src/lib/products-config.ts +73 -0
  222. package/src/providers/booking-dialog-provider.tsx +107 -38
  223. package/src/providers/bottom-sheet-provider.tsx +40 -0
  224. package/src/providers/dependent-add-on-dialog-provider.tsx +105 -0
  225. package/src/radius.css +5 -0
  226. package/src/spacing.css +7 -0
  227. package/src/strings/en.json +1774 -0
  228. package/src/strings/es.json +1573 -0
  229. package/src/strings/fr.json +1573 -0
  230. package/src/strings/index.js +23 -0
  231. package/src/text-style.css +97 -0
  232. package/src/types/fareharbor.d.ts +12 -0
  233. package/src/types/quiz.ts +59 -0
  234. package/src/utils/currency-converter.ts +101 -0
  235. package/src/components/BookingFlow.tsx +0 -2952
  236. package/src/components/LanguageSwitcher.tsx +0 -30
  237. package/src/components/PrivateShuttleBookingFlow.tsx +0 -2290
  238. package/src/components/ProductList.tsx +0 -78
  239. package/src/components/WhatsAppPhoneInput.tsx +0 -224
  240. package/src/components/index.ts +0 -31
  241. package/src/lib/api.ts +0 -801
  242. package/src/lib/booking-api-auth.ts +0 -9
  243. package/src/lib/checkout-breakdown.test.ts +0 -70
  244. package/src/types/google-maps.d.ts +0 -2
  245. /package/src/components/{CurrencySwitcher.tsx → booking/CurrencySwitcher.tsx} +0 -0
  246. /package/src/components/{ErrorBoundary.tsx → booking/ErrorBoundary.tsx} +0 -0
  247. /package/src/lib/{i18n → booking/i18n}/config.ts +0 -0
  248. /package/src/lib/{i18n → booking/i18n}/index.tsx +0 -0
  249. /package/src/lib/{marker-icons.ts → booking/marker-icons.ts} +0 -0
  250. /package/src/lib/{places-api.ts → booking/places-api.ts} +0 -0
  251. /package/src/lib/{theme.ts → booking/theme.ts} +0 -0
  252. /package/src/lib/{utils.ts → booking/utils.ts} +0 -0
@@ -0,0 +1,273 @@
1
+ 'use client';
2
+
3
+ import { useState, useMemo } from 'react';
4
+ import { useTranslations, useLocale } from '@/lib/booking/i18n';
5
+ import { getProductDescription } from '@/lib/product-descriptions';
6
+ import PlusIcon from '@/assets/icons/plus.svg';
7
+ import MinusIcon from '@/assets/icons/minus.svg';
8
+ import styles from './TourDescription.module.css';
9
+
10
+ export interface TourDescriptionReview {
11
+ text: string;
12
+ name: string;
13
+ }
14
+
15
+ /** Grouped bullets under a bold subheading inside an expandable section */
16
+ export interface TourDescriptionSubsection {
17
+ title: string;
18
+ items: string[];
19
+ }
20
+
21
+ export interface TourDescriptionSection {
22
+ title: string;
23
+ /** String for paragraph; string[] for bullet list or line-by-line HTML; subsection[] for titled bullet groups */
24
+ content:
25
+ | string
26
+ | string[]
27
+ | TourDescriptionSubsection[]
28
+ | React.ReactNode;
29
+ }
30
+
31
+ export interface TourDescriptionProps {
32
+ /** Product slug (e.g. moraine-lake-sunrise) - loads content from product-descriptions/{slug}.{locale}.json */
33
+ productSlug?: string;
34
+ /** Locale for loading translated content (defaults to useLocale) */
35
+ locale?: string;
36
+ /** Override main toggle label (default: “See full tour description”) */
37
+ toggleLabel?: string;
38
+ /** Override: main description paragraphs (if provided, skips file load) */
39
+ paragraphs?: string[];
40
+ /** Override: highlighted review (if provided, skips file load for review) */
41
+ review?: TourDescriptionReview;
42
+ /** Override: nested expandable sections (if provided, skips file load) */
43
+ sections?: TourDescriptionSection[];
44
+ /** Start expanded (e.g. when in partial/pre-launch state) */
45
+ defaultExpanded?: boolean;
46
+ }
47
+
48
+ /** Items starting with • - * are bullet list; otherwise join as HTML (multi-line format) */
49
+ function isBulletList(items: string[]): boolean {
50
+ const bulletChars = ['•', '-', '*'];
51
+ return items.length > 0 && items.every((item) => bulletChars.some((c) => item.trim().startsWith(c)));
52
+ }
53
+
54
+ /** Single item is a bullet (starts with •, -, or *) */
55
+ function isBulletItem(item: string): boolean {
56
+ const bulletChars = ['•', '-', '*'];
57
+ return bulletChars.some((c) => item.trim().startsWith(c));
58
+ }
59
+
60
+ /** True if the entire line is just a single tag (e.g. <strong>Heading</strong>), not mixed content */
61
+ function isHeadingLine(line: string): boolean {
62
+ const trimmed = line.trim();
63
+ return /^<[a-zA-Z]+>[^<]*<\/[a-zA-Z]+>$/.test(trimmed);
64
+ }
65
+
66
+ function isSubsectionGroups(
67
+ content: TourDescriptionSection['content']
68
+ ): content is TourDescriptionSubsection[] {
69
+ if (!Array.isArray(content) || content.length === 0) return false;
70
+ const first = content[0];
71
+ return (
72
+ typeof first === 'object' &&
73
+ first !== null &&
74
+ 'title' in first &&
75
+ 'items' in first &&
76
+ typeof (first as TourDescriptionSubsection).title === 'string' &&
77
+ Array.isArray((first as TourDescriptionSubsection).items)
78
+ );
79
+ }
80
+
81
+ function ExpandableSection({
82
+ title,
83
+ children,
84
+ isOpen,
85
+ onToggle,
86
+ }: {
87
+ title: string;
88
+ children: React.ReactNode;
89
+ isOpen: boolean;
90
+ onToggle: () => void;
91
+ }) {
92
+ return (
93
+ <div className={styles.nestedSection}>
94
+ <button
95
+ type="button"
96
+ className={styles.nestedToggle}
97
+ onClick={onToggle}
98
+ aria-expanded={isOpen}
99
+ >
100
+ <span>{title}</span>
101
+ <svg
102
+ className={styles.chevron}
103
+ width="16"
104
+ height="16"
105
+ viewBox="0 0 24 24"
106
+ fill="none"
107
+ stroke="currentColor"
108
+ strokeWidth="2"
109
+ >
110
+ <path d="M9 6l6 6-6 6" />
111
+ </svg>
112
+ </button>
113
+ <div className={`${styles.nestedContentWrapper} ${isOpen ? styles.nestedContentOpen : ''}`}>
114
+ <div className={styles.nestedContent}>{children}</div>
115
+ </div>
116
+ </div>
117
+ );
118
+ }
119
+
120
+ export function TourDescription({
121
+ productSlug,
122
+ locale: localeProp,
123
+ toggleLabel,
124
+ paragraphs: paragraphsProp,
125
+ review: reviewProp,
126
+ sections: sectionsProp,
127
+ defaultExpanded = false,
128
+ }: TourDescriptionProps) {
129
+ const { t } = useTranslations();
130
+ const { locale: contextLocale } = useLocale();
131
+ const [isExpanded, setIsExpanded] = useState(defaultExpanded);
132
+ const [openSectionIndex, setOpenSectionIndex] = useState<number | null>(null);
133
+
134
+ const locale = localeProp ?? contextLocale;
135
+
136
+ const loadedContent = useMemo(() => {
137
+ if (productSlug && locale) {
138
+ return getProductDescription(productSlug, locale);
139
+ }
140
+ return null;
141
+ }, [productSlug, locale]);
142
+
143
+ const paragraphs = paragraphsProp ?? loadedContent?.paragraphs ?? [];
144
+ const review = reviewProp ?? loadedContent?.review;
145
+ const sections = sectionsProp ?? loadedContent?.sections ?? [];
146
+
147
+ const displayParagraphs =
148
+ paragraphs.length > 0 ? paragraphs : [];
149
+ const displayReview = review ?? null;
150
+ const displaySections =
151
+ sections.length > 0 ? sections : [];
152
+
153
+ /** Group paragraphs: consecutive bullet items become a single <ul> block */
154
+ const paragraphBlocks = useMemo(() => {
155
+ const blocks: ({ type: 'paragraph'; text: string } | { type: 'bulletList'; items: string[] })[] = [];
156
+ let i = 0;
157
+ while (i < displayParagraphs.length) {
158
+ const p = displayParagraphs[i];
159
+ if (isBulletItem(p)) {
160
+ const items: string[] = [];
161
+ while (i < displayParagraphs.length && isBulletItem(displayParagraphs[i])) {
162
+ items.push(displayParagraphs[i]);
163
+ i++;
164
+ }
165
+ blocks.push({ type: 'bulletList', items });
166
+ } else {
167
+ blocks.push({ type: 'paragraph', text: p });
168
+ i++;
169
+ }
170
+ }
171
+ return blocks;
172
+ }, [displayParagraphs]);
173
+
174
+ return (
175
+ <div className={styles.root}>
176
+ <button
177
+ type="button"
178
+ className={styles.mainToggle}
179
+ onClick={() => setIsExpanded((e) => !e)}
180
+ aria-expanded={isExpanded}
181
+ >
182
+ <span>{toggleLabel ?? t('booking.seeFullTourDescription')}</span>
183
+ <span className={`${styles.toggleIcon} ${isExpanded ? styles.toggleIconExpanded : ''}`} aria-hidden>
184
+ {isExpanded ? (
185
+ <MinusIcon />
186
+ ) : (
187
+ <PlusIcon />
188
+ )}
189
+ </span>
190
+ </button>
191
+
192
+ <div className={`${styles.contentWrapper} ${isExpanded ? styles.contentExpanded : ''}`}>
193
+ <div className={styles.content}>
194
+ {paragraphBlocks.map((block, i) =>
195
+ block.type === 'paragraph' ? (
196
+ <p key={i} className={styles.paragraph} dangerouslySetInnerHTML={{ __html: block.text }} />
197
+ ) : (
198
+ <ul key={i} className={styles.bulletList}>
199
+ {block.items.map((item, j) => (
200
+ <li key={j} className={styles.bulletItem} dangerouslySetInnerHTML={{ __html: item }} />
201
+ ))}
202
+ </ul>
203
+ )
204
+ )}
205
+ {displayReview && (
206
+ <blockquote className={styles.review}>
207
+ <p className={styles.reviewText} dangerouslySetInnerHTML={{ __html: displayReview.text }} />
208
+ <cite className={styles.reviewName} dangerouslySetInnerHTML={{ __html: displayReview.name }} />
209
+ </blockquote>
210
+ )}
211
+ {displaySections.map((section, i) => (
212
+ <ExpandableSection
213
+ key={i}
214
+ title={section.title}
215
+ isOpen={openSectionIndex === i}
216
+ onToggle={() => setOpenSectionIndex((prev) => (prev === i ? null : i))}
217
+ >
218
+ {isSubsectionGroups(section.content) ? (
219
+ <div className={styles.sectionSubsections}>
220
+ {section.content.map((sub, k) => (
221
+ <div key={k} className={styles.sectionSubsection}>
222
+ <div
223
+ className={styles.sectionSubsectionTitle}
224
+ dangerouslySetInnerHTML={{ __html: sub.title }}
225
+ />
226
+ {sub.items.length > 0 && (
227
+ <ul className={styles.bulletList}>
228
+ {sub.items.map((item, j) => (
229
+ <li
230
+ key={j}
231
+ className={styles.bulletItem}
232
+ dangerouslySetInnerHTML={{ __html: item }}
233
+ />
234
+ ))}
235
+ </ul>
236
+ )}
237
+ </div>
238
+ ))}
239
+ </div>
240
+ ) : Array.isArray(section.content) ? (
241
+ isBulletList(section.content) ? (
242
+ <ul className={styles.bulletList}>
243
+ {section.content.map((item, j) => (
244
+ <li key={j} className={styles.bulletItem} dangerouslySetInnerHTML={{ __html: item }} />
245
+ ))}
246
+ </ul>
247
+ ) : (
248
+ <div className={styles.sectionHtmlLines}>
249
+ {section.content.map((item, j) => (
250
+ <div
251
+ key={j}
252
+ className={
253
+ isHeadingLine(item)
254
+ ? styles.sectionLineHeading
255
+ : styles.sectionLineIndented
256
+ }
257
+ dangerouslySetInnerHTML={{ __html: item }}
258
+ />
259
+ ))}
260
+ </div>
261
+ )
262
+ ) : typeof section.content === 'string' ? (
263
+ <div className={styles.sectionHtml} dangerouslySetInnerHTML={{ __html: section.content }} />
264
+ ) : (
265
+ section.content
266
+ )}
267
+ </ExpandableSection>
268
+ ))}
269
+ </div>
270
+ </div>
271
+ </div>
272
+ );
273
+ }
@@ -1,20 +1,34 @@
1
1
  /**
2
2
  * Optional UI/behavior overrides for booking flows.
3
+ * Omitted fields keep main-site / change-booking defaults.
3
4
  */
4
5
  export interface BookingFlowUiOptions {
5
6
  showCollage?: boolean;
6
7
  showTourDescription?: boolean;
7
8
  autoSelectFirstAvailableDate?: boolean;
8
9
  autoSelectFirstHighlightedPickup?: boolean;
10
+ /**
11
+ * Partner portal: org has DEFERRED_INVOICE — confirm via `/1/partner/confirm-booking-without-payment`
12
+ * (Bearer from [setPartnerPortalBookingJwtGetter]) instead of Stripe checkout.
13
+ */
9
14
  partnerDeferredInvoice?: boolean;
15
+ /** Optional submit label override when `partnerDeferredInvoice` is enabled. */
10
16
  partnerDeferredInvoiceSubmitLabel?: string;
17
+ /** Optional attribution summary shown at checkout (e.g. partner + agent). */
11
18
  partnerAttributionSummary?: string;
19
+ /** Optional required confirmation checkbox label for attribution summary. */
12
20
  partnerAttributionConfirmLabel?: string;
21
+ /**
22
+ * Extra inset (px) from the top of the viewport for itinerary “sticky” detection and CSS `top`
23
+ * when a host page has its own sticky chrome (e.g. partner portal header + tabs).
24
+ */
13
25
  itineraryStickyTopOffsetPx?: number;
14
26
  }
15
27
 
16
28
  /**
17
- * Baseline UX for embedded partner-style surfaces.
29
+ * Baseline UX for embedded partner-style surfaces (partner portal Book tab, TicketBooth provider dashboard):
30
+ * skip collage + tour description, auto-pick the earliest bookable calendar day and first time slot,
31
+ * optionally auto-select a highlighted pickup when `highlightedPickupLocationIds` is set on the page.
18
32
  */
19
33
  export const PARTNER_EMBEDDED_BOOKING_FLOW_UI_BASE: BookingFlowUiOptions = {
20
34
  showCollage: false,