@ticketboothapp/booking 0.1.19 → 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 (154) 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/InfoTooltip.tsx +2 -13
  8. package/src/components/booking/PickupLocationSelector.tsx +2 -2
  9. package/src/components/booking/PriceBreakdown.tsx +11 -34
  10. package/src/index.ts +3 -1
  11. package/src/app/photo-sessions/photo-packages.ts +0 -75
  12. package/src/assets/icons/minus.svg +0 -7
  13. package/src/assets/icons/partner-logos/getyourguide.svg +0 -8
  14. package/src/assets/icons/plus.svg +0 -3
  15. package/src/colours.css +0 -23
  16. package/src/components/BookingDetails.module.css +0 -1591
  17. package/src/components/BookingDetails.tsx +0 -2264
  18. package/src/components/JobApplicationDialog.module.css +0 -440
  19. package/src/components/JobApplicationDialog.tsx +0 -620
  20. package/src/components/PhoneInputWithCountry.module.css +0 -131
  21. package/src/components/PhoneInputWithCountry.tsx +0 -44
  22. package/src/components/PickupLocationDialog.module.css +0 -360
  23. package/src/components/PickupLocationDialog.tsx +0 -357
  24. package/src/components/PickupLocationMap.tsx +0 -110
  25. package/src/components/PostBookingDependentAddOnUpsell.module.css +0 -174
  26. package/src/components/PostBookingDependentAddOnUpsell.tsx +0 -407
  27. package/src/components/accordion.css +0 -27
  28. package/src/components/accordion.tsx +0 -29
  29. package/src/components/analytics/AnalyticsConsentRestore.tsx +0 -19
  30. package/src/components/analytics/AnalyticsScripts.tsx +0 -106
  31. package/src/components/analytics/CookieConsentBanner.css +0 -86
  32. package/src/components/analytics/CookieConsentBanner.tsx +0 -102
  33. package/src/components/bottom-sheet.module.css +0 -78
  34. package/src/components/bottom-sheet.tsx +0 -60
  35. package/src/components/breadcrumb.module.css +0 -40
  36. package/src/components/breadcrumb.tsx +0 -36
  37. package/src/components/button.css +0 -245
  38. package/src/components/button.tsx +0 -152
  39. package/src/components/client-bottom-sheet.tsx +0 -14
  40. package/src/components/colorable-svg.tsx +0 -29
  41. package/src/components/conditional-footer.tsx +0 -27
  42. package/src/components/contact-us.module.css +0 -147
  43. package/src/components/contact-us.tsx +0 -49
  44. package/src/components/email-signup.css +0 -151
  45. package/src/components/email-signup.tsx +0 -63
  46. package/src/components/faq-wrapper.module.css +0 -47
  47. package/src/components/faq-wrapper.tsx +0 -15
  48. package/src/components/footer.css +0 -187
  49. package/src/components/footer.tsx +0 -143
  50. package/src/components/global-simple-modal.tsx +0 -33
  51. package/src/components/google-review-summary.module.css +0 -77
  52. package/src/components/google-review-summary.tsx +0 -50
  53. package/src/components/hero-image.css +0 -13
  54. package/src/components/hero-image.tsx +0 -44
  55. package/src/components/image.css +0 -29
  56. package/src/components/image.tsx +0 -113
  57. package/src/components/language-aware-link.tsx +0 -72
  58. package/src/components/language-switcher.module.css +0 -124
  59. package/src/components/language-switcher.tsx +0 -75
  60. package/src/components/map-section.css +0 -59
  61. package/src/components/map-section.tsx +0 -63
  62. package/src/components/navbar.module.css +0 -152
  63. package/src/components/navbar.tsx +0 -125
  64. package/src/components/parallax-provider.tsx +0 -11
  65. package/src/components/product-tag.module.css +0 -30
  66. package/src/components/product-tag.tsx +0 -34
  67. package/src/components/product-theme-pages/best-option.module.css +0 -70
  68. package/src/components/product-theme-pages/best-option.tsx +0 -35
  69. package/src/components/product-theme-pages/extended-tour-options.module.css +0 -22
  70. package/src/components/product-theme-pages/extended-tour-options.tsx +0 -11
  71. package/src/components/product-theme-pages/image-modal.tsx +0 -248
  72. package/src/components/product-theme-pages/photo-gallery.module.css +0 -200
  73. package/src/components/product-theme-pages/photo-gallery.tsx +0 -90
  74. package/src/components/product-theme-pages/product-theme-page-layout.module.css +0 -13
  75. package/src/components/product-theme-pages/product-theme-page-layout.tsx +0 -67
  76. package/src/components/product-theme-pages/top-of-fold.module.css +0 -179
  77. package/src/components/product-theme-pages/top-of-fold.tsx +0 -80
  78. package/src/components/product-tile/image-only-product-tile-desktop.module.css +0 -106
  79. package/src/components/product-tile/image-only-product-tile-desktop.tsx +0 -56
  80. package/src/components/product-tile/image-only-product-tile-mobile.module.css +0 -122
  81. package/src/components/product-tile/image-only-product-tile-mobile.tsx +0 -89
  82. package/src/components/product-tile/image-only-product-tile.tsx +0 -44
  83. package/src/components/product-tile/product-tile-card.module.css +0 -84
  84. package/src/components/product-tile/product-tile-card.tsx +0 -61
  85. package/src/components/review-highlights-section.css +0 -85
  86. package/src/components/review-highlights-section.tsx +0 -127
  87. package/src/components/season-closure-overlay.module.css +0 -99
  88. package/src/components/season-closure-overlay.tsx +0 -98
  89. package/src/components/simple-modal.tsx +0 -69
  90. package/src/components/simple-top-of-fold.module.css +0 -76
  91. package/src/components/simple-top-of-fold.tsx +0 -34
  92. package/src/components/spacer.css +0 -41
  93. package/src/components/spacer.tsx +0 -23
  94. package/src/components/star-rating.module.css +0 -74
  95. package/src/components/star-rating.tsx +0 -48
  96. package/src/components/terms/TermsContent.tsx +0 -178
  97. package/src/components/title-subtitle.module.css +0 -10
  98. package/src/components/title-subtitle.tsx +0 -30
  99. package/src/components/translatable-reviews.tsx +0 -75
  100. package/src/components/value-pill.module.css +0 -59
  101. package/src/components/value-pill.tsx +0 -46
  102. package/src/components/value-props.css +0 -185
  103. package/src/components/value-props.tsx +0 -88
  104. package/src/constants/booking-guide-quiz.ts +0 -64
  105. package/src/constants/contact-info.ts +0 -2
  106. package/src/constants/faq.ts +0 -44
  107. package/src/constants/images.ts +0 -556
  108. package/src/constants/json-ld/faq-json-ld.tsx +0 -170
  109. package/src/constants/json-ld/homepage-json-ld.tsx +0 -138
  110. package/src/constants/json-ld/job-posting-json-ld.tsx +0 -92
  111. package/src/constants/json-ld/organization-json-ld.tsx +0 -62
  112. package/src/constants/json-ld/page-json-ld.tsx +0 -6
  113. package/src/constants/json-ld/product-json-ld.tsx +0 -154
  114. package/src/constants/json-ld/review-json-ld.tsx +0 -377
  115. package/src/constants/navigation-links/footer-links.ts +0 -48
  116. package/src/constants/navigation-links/nav-bar-links.ts +0 -41
  117. package/src/constants/navigation-links/navigation-link.ts +0 -6
  118. package/src/constants/pill-values.ts +0 -210
  119. package/src/constants/products.ts +0 -155
  120. package/src/constants/quiz-recommendations.ts +0 -506
  121. package/src/constants/reviews.ts +0 -75
  122. package/src/constants/staff.ts +0 -197
  123. package/src/constants/value-props.ts +0 -58
  124. package/src/data/dap-descriptions/session-couples-families-friends.en.json +0 -61
  125. package/src/data/dap-descriptions/session-elopements.en.json +0 -60
  126. package/src/data/dap-descriptions/session-proposals.en.json +0 -60
  127. package/src/data/product-descriptions/afternoon-delight.en.json +0 -35
  128. package/src/data/product-descriptions/emerald-lake-escape.en.json +0 -68
  129. package/src/data/product-descriptions/lake-louise-adventure.en.json +0 -74
  130. package/src/data/product-descriptions/moraine-lake-adventure.en.json +0 -78
  131. package/src/data/product-descriptions/moraine-lake-sunrise-lake-louise-golden-hour.en.json +0 -65
  132. package/src/data/product-descriptions/moraine-lake-sunrise.en.json +0 -64
  133. package/src/data/product-descriptions/private-tour.en.json +0 -80
  134. package/src/data/product-descriptions/two-lakes-combo.en.json +0 -65
  135. package/src/data/products-config.json +0 -101
  136. package/src/hooks/use-bottom-sheet.tsx +0 -15
  137. package/src/hooks/use-simple-modal.tsx +0 -27
  138. package/src/hooks/useBookingSourceMetadataFromLocation.ts +0 -21
  139. package/src/hooks/useEmailSubscription.tsx +0 -103
  140. package/src/hooks/useEmbeddedInIframe.ts +0 -16
  141. package/src/hooks/useIsBookingLaunchLive.ts +0 -49
  142. package/src/hooks/useQuiz.tsx +0 -210
  143. package/src/providers/bottom-sheet-provider.tsx +0 -40
  144. package/src/providers/dependent-add-on-dialog-provider.tsx +0 -105
  145. package/src/radius.css +0 -5
  146. package/src/spacing.css +0 -7
  147. package/src/strings/en.json +0 -1774
  148. package/src/strings/es.json +0 -1573
  149. package/src/strings/fr.json +0 -1573
  150. package/src/strings/index.js +0 -23
  151. package/src/text-style.css +0 -97
  152. package/src/types/fareharbor.d.ts +0 -12
  153. package/src/types/quiz.ts +0 -59
  154. package/src/utils/currency-converter.ts +0 -101
@@ -1,102 +0,0 @@
1
- 'use client';
2
-
3
- import { useState, useEffect } from 'react';
4
- import { usePathname } from 'next/navigation';
5
- import { useEmbeddedInIframe } from '@/hooks/useEmbeddedInIframe';
6
- import { updateAnalyticsConsent } from './AnalyticsScripts';
7
- import { ENV, isLocalhost, isProduction, isStaging } from '@/lib/env';
8
- import Link from 'next/link';
9
- import './CookieConsentBanner.css';
10
-
11
- const CONSENT_KEY = 'cookie-consent';
12
-
13
- /** Show banner when we load GA4/Meta (production), or on localhost/staging for testing. */
14
- function shouldShowBanner(): boolean {
15
- const hasAnalyticsIds = !!ENV.GA4_MEASUREMENT_ID || !!ENV.META_PIXEL_ID;
16
- if (isProduction() && !isLocalhost() && hasAnalyticsIds) return true;
17
- if (isLocalhost() || isStaging()) return true; // show for testing
18
- return false;
19
- }
20
-
21
- export function CookieConsentBanner() {
22
- const pathname = usePathname();
23
- const embeddedInIframe = useEmbeddedInIframe();
24
- const [mounted, setMounted] = useState(false);
25
- const [showBanner, setShowBanner] = useState(false);
26
-
27
- useEffect(() => {
28
- setMounted(true);
29
- }, []);
30
-
31
- useEffect(() => {
32
- if (!mounted || typeof window === 'undefined') return;
33
- if (!shouldShowBanner()) return;
34
- try {
35
- // On localhost/staging: always show for testing. On production: only when user hasn't accepted.
36
- const alwaysShowForTesting = isLocalhost() || isStaging();
37
- const stored = localStorage.getItem(CONSENT_KEY);
38
- setShowBanner(alwaysShowForTesting || stored !== 'granted');
39
- } catch {
40
- setShowBanner(true);
41
- }
42
- }, [mounted]);
43
-
44
- // Signal to other UI (e.g. floating book button) that banner is visible, so they can position above it
45
- useEffect(() => {
46
- if (typeof document === 'undefined') return;
47
- if (showBanner && !embeddedInIframe && pathname !== '/live-pickups') {
48
- document.body.dataset.cookieBannerVisible = 'true';
49
- } else {
50
- delete document.body.dataset.cookieBannerVisible;
51
- }
52
- return () => {
53
- delete document.body.dataset.cookieBannerVisible;
54
- };
55
- }, [showBanner, embeddedInIframe, pathname]);
56
-
57
- const handleAccept = () => {
58
- updateAnalyticsConsent(true);
59
- setShowBanner(false);
60
- };
61
-
62
- const handleDecline = () => {
63
- try {
64
- localStorage.setItem(CONSENT_KEY, 'denied');
65
- } catch {
66
- /* ignore */
67
- }
68
- setShowBanner(false);
69
- };
70
-
71
- if (embeddedInIframe || pathname === '/live-pickups' || !showBanner || !mounted) return null;
72
-
73
- return (
74
- <div
75
- className="cookieConsentBanner"
76
- role="dialog"
77
- aria-label="Cookie consent"
78
- >
79
- <div className="cookieConsentBannerInner">
80
- <p className="cookieConsentBannerText">
81
- Accept to approve the use of cookies for analytics and advertising. See our <Link href="/privacy-policy" className="cookieConsentBannerLink">privacy policy</Link> for details.
82
- </p>
83
- <div className="cookieConsentBannerButtons">
84
- <button
85
- type="button"
86
- onClick={handleDecline}
87
- className="cookieConsentBannerDecline"
88
- >
89
- Decline
90
- </button>
91
- <button
92
- type="button"
93
- onClick={handleAccept}
94
- className="cookieConsentBannerAccept"
95
- >
96
- Accept
97
- </button>
98
- </div>
99
- </div>
100
- </div>
101
- );
102
- }
@@ -1,78 +0,0 @@
1
- .container {
2
- position: fixed;
3
- top: 0;
4
- left: 0;
5
- right: 0;
6
- bottom: 0;
7
- z-index: 9999;
8
- display: flex;
9
- flex-direction: column;
10
- pointer-events: none;
11
- }
12
-
13
- .overlay {
14
- position: absolute;
15
- top: 0;
16
- left: 0;
17
- right: 0;
18
- bottom: 0;
19
- background: var(--accent-orange-70);
20
- pointer-events: auto;
21
- backdrop-filter: blur(4px);
22
- }
23
-
24
- .overlayVisible {
25
- opacity: 1;
26
- }
27
-
28
- .sheet {
29
- position: absolute;
30
- left: 0;
31
- right: 0;
32
- bottom: 0;
33
- border-radius: 24px 24px 0 0;
34
- pointer-events: auto;
35
- height: 100vh;
36
- display: flex;
37
- flex-direction: column;
38
- overflow: hidden;
39
- margin: 0;
40
- padding: 0;
41
- }
42
-
43
- .sheetOpen {
44
- transform: translateY(0);
45
- }
46
-
47
- .header {
48
- position: sticky;
49
- top: 0;
50
- background: transparent;
51
- backdrop-filter: blur(8px);
52
- display: flex;
53
- justify-content: center;
54
- z-index: 1;
55
- padding: 1rem 0;
56
- }
57
-
58
- .closeButton {
59
- width: 40px;
60
- height: 40px;
61
- border-radius: 50%;
62
- border: none;
63
- background: var(--accent-orange);
64
- color: white;
65
- font-size: 24px;
66
- cursor: pointer;
67
- display: flex;
68
- align-items: center;
69
- justify-content: center;
70
- }
71
-
72
- .content {
73
- flex: 1;
74
- padding: 1rem 1rem 0 1rem;
75
- background: var(--light-orange-background);
76
- border-radius: 24px 24px 0 0;
77
- overflow-y: auto;
78
- }
@@ -1,60 +0,0 @@
1
- 'use client';
2
-
3
- import { useEffect, useState } from 'react';
4
- import styles from './bottom-sheet.module.css';
5
- import { motion, animate } from "motion/react";
6
-
7
- interface BottomSheetProps {
8
- isOpen: boolean;
9
- onClose: () => void;
10
- children: React.ReactNode;
11
- }
12
-
13
- export default function BottomSheet({ isOpen, onClose, children }: BottomSheetProps) {
14
- const [isAnimating, setIsAnimating] = useState(isOpen);
15
-
16
- useEffect(() => {
17
- if (!isOpen) return;
18
-
19
- setIsAnimating(true);
20
- document.body.style.overflow = 'hidden';
21
- animate(".bottom-sheet", { y: ["100%", "0%"] }, { duration: 0.5 });
22
- animate(".overlay", { opacity: [0, 1] }, { duration: 0.3 });
23
-
24
- return () => {
25
- document.body.style.overflow = 'unset';
26
- };
27
- }, [isOpen]);
28
-
29
- useEffect(() => {
30
- if (!isOpen && isAnimating) {
31
- animate(".bottom-sheet", { y: ["0%", "100%"] }, { duration: 0.5 });
32
- animate(".overlay", { opacity: [1, 0] }, { duration: 0.3 });
33
- const timer = setTimeout(() => setIsAnimating(false), 500);
34
- return () => clearTimeout(timer);
35
- }
36
- }, [isOpen, isAnimating]);
37
-
38
- if (!isOpen && !isAnimating) return null;
39
-
40
- return (
41
- <div className={styles.container}>
42
- <motion.div
43
- className={`${styles.overlay} overlay`}
44
- onClick={onClose}
45
- />
46
- <motion.div
47
- className={`${styles.sheet} bottom-sheet`}
48
- >
49
- <div className={styles.header}>
50
- <button className={styles.closeButton} onClick={onClose}>
51
- <span>×</span>
52
- </button>
53
- </div>
54
- <div className={styles.content}>
55
- {children}
56
- </div>
57
- </motion.div>
58
- </div>
59
- );
60
- }
@@ -1,40 +0,0 @@
1
- .breadcrumb {
2
- padding: 1rem 0;
3
- margin: 0 auto;
4
- max-width: var(--max-content-width);
5
- }
6
-
7
- .breadcrumb ol {
8
- list-style: none;
9
- margin: 0;
10
- padding: 0;
11
- display: flex;
12
- flex-wrap: wrap;
13
- gap: 0.25rem;
14
- }
15
-
16
- .breadcrumb li {
17
- display: flex;
18
- align-items: center;
19
- color: var(--primary-text);
20
- }
21
-
22
- .breadcrumb li:not(:last-child)::after {
23
- content: '/';
24
- margin: 0 0.25rem;
25
- color: var(--secondary-text);
26
- }
27
-
28
- .breadcrumb a {
29
- color: var(--primary-text);
30
- text-decoration: none;
31
- transition: color 0.2s ease;
32
- }
33
-
34
- .breadcrumb a:hover {
35
- color: var(--accent-turquoise);
36
- }
37
-
38
- .breadcrumb span {
39
- color: var(--grey-text);
40
- }
@@ -1,36 +0,0 @@
1
- 'use client';
2
-
3
- import Link from 'next/link';
4
- import { usePathname } from 'next/navigation';
5
- import styles from './breadcrumb.module.css';
6
-
7
- export default function Breadcrumb() {
8
- const pathname = usePathname();
9
- const paths = pathname.split('/').filter(Boolean);
10
-
11
- return (
12
- <nav className={styles.breadcrumb} aria-label="Breadcrumb">
13
- <ol>
14
- <li>
15
- <Link href="/">Home</Link>
16
- </li>
17
- {paths.map((path, index) => {
18
- const href = `/${paths.slice(0, index + 1).join('/')}`;
19
- const isLast = index === paths.length - 1;
20
-
21
- return (
22
- <li key={href}>
23
- {isLast ? (
24
- <span>{path.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}</span>
25
- ) : (
26
- <Link href={href}>
27
- {path.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}
28
- </Link>
29
- )}
30
- </li>
31
- );
32
- })}
33
- </ol>
34
- </nav>
35
- );
36
- }
@@ -1,245 +0,0 @@
1
- .button {
2
- padding: 8px 16px;
3
- border-radius: 24px;
4
- border: none;
5
- font-size: 1rem;
6
- cursor: pointer;
7
- transition: all 0.2s ease-in-out;
8
- font-weight: 500;
9
- position: relative;
10
- overflow: hidden;
11
- z-index: 1;
12
- font-family: 'Poppins', sans-serif;
13
- font-weight: 700;
14
- text-transform: lowercase;
15
- display: inline-block;
16
- }
17
-
18
- .button-primary {
19
- background-color: var(--accent-orange);
20
- color: var(--accent-white);
21
- }
22
-
23
- .button-secondary {
24
- background-color: var(--accent-white);
25
- color: var(--accent-orange);
26
- }
27
-
28
- .button-disabled {
29
- background-color: var(--grey-text);
30
- color: var(--accent-white);
31
- cursor: not-allowed;
32
- }
33
-
34
- .button-disabled:hover {
35
- background-color: var(--grey-text);
36
- color: var(--accent-white) !important;
37
- }
38
-
39
- .button-outline {
40
- background-color: transparent;
41
- color: var(--accent-white);
42
- border: 2px solid var(--accent-white);
43
- box-sizing: border-box;
44
- padding: 6px 14px;
45
- }
46
-
47
- .button-large {
48
- font-size: 1.2rem;
49
- padding: 1rem 2rem;
50
- border-radius: 40px;
51
- }
52
-
53
- /* Desktop hover */
54
- @media (min-width: 1024px) {
55
- .button::before {
56
- content: '';
57
- position: absolute;
58
- top: 0;
59
- left: -100%;
60
- width: 100%;
61
- height: 100%;
62
- border-radius: 24px;
63
- transition: all 0.3s ease-in-out;
64
- z-index: -1;
65
- }
66
-
67
- .button-primary:hover::before {
68
- left: 0;
69
- }
70
-
71
- .button-primary.hover-white::before {
72
- background-color: var(--accent-white);
73
- }
74
-
75
- .button-primary.hover-turquoise::before {
76
- background-color: var(--accent-turquoise);
77
- }
78
-
79
- .button-primary.hover-orange::before {
80
- background-color: var(--accent-orange);
81
- }
82
-
83
- .button-primary.hover-orange:hover {
84
- color: var(--accent-white);
85
- }
86
-
87
- .button-primary.hover-turquoise:hover {
88
- color: var(--accent-white);
89
- }
90
-
91
- .button-secondary:hover::before {
92
- left: 0;
93
- }
94
-
95
- .button-secondary::before {
96
- background-color: var(--accent-turquoise);
97
- }
98
-
99
- .button-secondary.hover-orange::before {
100
- background-color: var(--accent-orange);
101
- }
102
-
103
- .button-secondary.hover-orange:hover {
104
- color: var(--accent-white) !important;
105
- }
106
-
107
- .button-secondary:hover {
108
- color: var(--accent-white) !important;
109
- }
110
-
111
- .button-outline::before {
112
- background-color: var(--accent-white);
113
- }
114
-
115
- .button-outline:hover::before {
116
- left: 0;
117
- }
118
-
119
- .button:hover {
120
- color: var(--accent-orange);
121
- }
122
- }
123
-
124
- /* Focus state for accessibility */
125
- .button:focus {
126
- outline: 2px solid var(--accent-orange);
127
- outline-offset: 2px;
128
- }
129
-
130
- .button-menu {
131
- background-color: var(--accent-orange);
132
- border-radius: 50px;
133
- padding: 8px 20px;
134
- display: flex;
135
- align-items: center;
136
- gap: 8px;
137
- }
138
-
139
- .button-menu .menu-text {
140
- color: white;
141
- font-weight: 400;
142
- text-transform: lowercase;
143
- font-size: 0.8rem;
144
- padding: 0.2rem;
145
- }
146
-
147
- .button-menu .menu-icon {
148
- display: flex;
149
- flex-direction: column;
150
- gap: 3px;
151
- position: relative;
152
- width: 12px;
153
- height: 12px;
154
- transition: transform 0.3s ease;
155
- }
156
-
157
- .button-menu .menu-icon span {
158
- display: block;
159
- width: 12px;
160
- height: 1.5px;
161
- background-color: white;
162
- border-radius: 2px;
163
- position: absolute;
164
- transition: transform 0.3s ease, opacity 0.3s ease;
165
- }
166
-
167
- /* Position the lines */
168
- .button-menu .menu-icon span:nth-child(1) {
169
- top: 0;
170
- }
171
-
172
- .button-menu .menu-icon span:nth-child(2) {
173
- top: 50%;
174
- transform: translateY(-50%);
175
- }
176
-
177
- .button-menu .menu-icon span:nth-child(3) {
178
- bottom: 0;
179
- }
180
-
181
- /* X animation when open with full rotation */
182
- .button-menu.open .menu-icon {
183
- transform: rotate(360deg);
184
- }
185
-
186
- .button-menu.open .menu-icon span:nth-child(1) {
187
- top: 50%;
188
- transform: translateY(-50%) rotate(45deg);
189
- }
190
-
191
- .button-menu.open .menu-icon span:nth-child(2) {
192
- opacity: 0;
193
- }
194
-
195
- .button-menu.open .menu-icon span:nth-child(3) {
196
- top: 50%;
197
- transform: translateY(-50%) rotate(-45deg);
198
- }
199
-
200
- /* Override booking-flow-preflight so Button component styles apply inside the booking flow */
201
- .booking-flow-preflight button.button {
202
- padding: 8px 16px;
203
- border-radius: 24px;
204
- border: none;
205
- font-size: 1rem;
206
- cursor: pointer;
207
- transition: all 0.2s ease-in-out;
208
- font-weight: 700;
209
- position: relative;
210
- overflow: hidden;
211
- z-index: 1;
212
- font-family: 'Poppins', sans-serif;
213
- text-transform: lowercase;
214
- display: inline-block;
215
- }
216
-
217
- .booking-flow-preflight button.button-primary {
218
- background-color: var(--accent-orange);
219
- color: var(--accent-white);
220
- }
221
-
222
- .booking-flow-preflight button.button-secondary {
223
- background-color: var(--accent-white);
224
- color: var(--accent-orange);
225
- }
226
-
227
- .booking-flow-preflight button.button-disabled {
228
- background-color: var(--grey-text);
229
- color: var(--accent-white);
230
- cursor: not-allowed;
231
- }
232
-
233
- .booking-flow-preflight button.button-outline {
234
- background-color: transparent;
235
- color: var(--accent-white);
236
- border: 2px solid var(--accent-white);
237
- box-sizing: border-box;
238
- padding: 6px 14px;
239
- }
240
-
241
- .booking-flow-preflight button.button-large {
242
- font-size: 1.2rem;
243
- padding: 1rem 2rem;
244
- border-radius: 40px;
245
- }
@@ -1,152 +0,0 @@
1
- "use client";
2
-
3
- import './button.css';
4
- import Link from 'next/link';
5
- import { OPEN_BOOKING_FOR_PRODUCT } from '@/providers/booking-dialog-provider';
6
-
7
- enum ButtonHoverColor {
8
- White = 'white',
9
- Turquoise = 'turquoise',
10
- Orange = 'orange'
11
- }
12
-
13
- enum PresetButtonActions {
14
- BOOK_ALL = 'BOOK_ALL',
15
- BOOK_MORAINE_LAKE_SUNRISE = 'BOOK_MORAINE_LAKE_SUNRISE',
16
- BOOK_MORAINE_LAKE_SUNRISE_LAKE_LOUISE_GOLDEN_HOUR = 'BOOK_MORAINE_LAKE_SUNRISE_LAKE_LOUISE_GOLDEN_HOUR',
17
- BOOK_TWO_LAKES_COMBO = 'BOOK_TWO_LAKES_COMBO',
18
- BOOK_MORAINE_LAKE_ADVENTURE = 'BOOK_MORAINE_LAKE_ADVENTURE',
19
- BOOK_LAKE_LOUISE_ADVENTURE = 'BOOK_LAKE_LOUISE_ADVENTURE',
20
- BOOK_EMERALD_LAKE_ESCAPE = 'BOOK_EMERALD_LAKE_ESCAPE',
21
- BOOK_PRIVATE_TOUR = 'BOOK_PRIVATE_TOUR',
22
- BOOK_THEME_SUNRISE = 'BOOK_THEME_SUNRISE',
23
- BOOK_THEME_MORAINE_LAKE = 'BOOK_THEME_MORAINE_LAKE',
24
- BOOK_THEME_LAKE_LOUISE = 'BOOK_THEME_LAKE_LOUISE',
25
- }
26
-
27
- /** Maps preset booking actions to product slugs. null = open product grid. */
28
- const PRESET_ACTION_TO_PRODUCT_SLUG: Partial<Record<PresetButtonActions, string | null>> = {
29
- [PresetButtonActions.BOOK_ALL]: null,
30
- [PresetButtonActions.BOOK_MORAINE_LAKE_SUNRISE]: 'moraine-lake-sunrise',
31
- [PresetButtonActions.BOOK_MORAINE_LAKE_SUNRISE_LAKE_LOUISE_GOLDEN_HOUR]: 'moraine-lake-sunrise-lake-louise-golden-hour',
32
- [PresetButtonActions.BOOK_TWO_LAKES_COMBO]: 'two-lakes-combo',
33
- [PresetButtonActions.BOOK_MORAINE_LAKE_ADVENTURE]: 'moraine-lake-adventure',
34
- [PresetButtonActions.BOOK_LAKE_LOUISE_ADVENTURE]: 'lake-louise-adventure',
35
- [PresetButtonActions.BOOK_EMERALD_LAKE_ESCAPE]: 'emerald-lake-escape',
36
- [PresetButtonActions.BOOK_PRIVATE_TOUR]: 'private-tour',
37
- [PresetButtonActions.BOOK_THEME_SUNRISE]: 'moraine-lake-sunrise-lake-louise-golden-hour',
38
- [PresetButtonActions.BOOK_THEME_MORAINE_LAKE]: 'moraine-lake-sunrise-lake-louise-golden-hour',
39
- [PresetButtonActions.BOOK_THEME_LAKE_LOUISE]: 'lake-louise-adventure',
40
- };
41
-
42
- interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
43
- children: React.ReactNode;
44
- variant?: 'primary' | 'secondary' | 'outline' | 'disabled';
45
- hoverColor?: ButtonHoverColor;
46
- isLarge?: boolean;
47
- action?: ButtonAction;
48
- shouldOpenInNewTab?: boolean;
49
- }
50
-
51
- type ButtonAction = PresetButtonActions | string;
52
-
53
- export default function Button({
54
- children,
55
- variant = 'primary',
56
- className,
57
- hoverColor = ButtonHoverColor.White,
58
- isLarge = false,
59
- action = '',
60
- shouldOpenInNewTab = false,
61
- ...props
62
- }: ButtonProps) {
63
- const finalAction = getFinalAction(action);
64
- const productSlug = getProductSlugForAction(action);
65
- const isBookingAction = isModalActionType(action);
66
-
67
- const handleClick = () => {
68
- if (isBookingAction) {
69
- if (productSlug) {
70
- window.dispatchEvent(new CustomEvent(OPEN_BOOKING_FOR_PRODUCT, { detail: { productId: productSlug } }));
71
- } else {
72
- window.dispatchEvent(new CustomEvent('openSimpleModal'));
73
- }
74
- }
75
- };
76
-
77
- return (
78
- <>
79
- {isBookingAction ? (
80
- <button
81
- onClick={handleClick}
82
- className={`button button-${variant} hover-${hoverColor} ${isLarge ? 'button-large' : ''} ${className}`}
83
- {...props}
84
- >
85
- {children}
86
- </button>
87
- ) : finalAction ? (
88
- <Link href={finalAction} target={shouldOpenInNewTab ? '_blank' : '_self'}>
89
- <button
90
- className={`button button-${variant} hover-${hoverColor} ${isLarge ? 'button-large' : ''} ${className}`}
91
- {...props}
92
- >
93
- {children}
94
- </button>
95
- </Link>
96
- ) : (
97
- <button
98
- className={`button button-${variant} hover-${hoverColor} ${isLarge ? 'button-large' : ''} ${className}`}
99
- {...props}
100
- >
101
- {children}
102
- </button>
103
- )}
104
- </>
105
- )
106
- }
107
-
108
- function isModalActionType(action: ButtonAction): boolean {
109
- return Object.values(PresetButtonActions).includes(action as PresetButtonActions);
110
- }
111
-
112
- function getProductSlugForAction(action: ButtonAction): string | null | undefined {
113
- if (Object.values(PresetButtonActions).includes(action as PresetButtonActions)) {
114
- return PRESET_ACTION_TO_PRODUCT_SLUG[action as PresetButtonActions] ?? null;
115
- }
116
- return undefined;
117
- }
118
-
119
- function getFinalAction(action: ButtonAction) {
120
- // Preset booking actions open the dialog (handled by handleClick)
121
- if (Object.values(PresetButtonActions).includes(action as PresetButtonActions)) {
122
- return null;
123
- }
124
-
125
- // Handle regular URLs (http or relative paths)
126
- if (typeof action === 'string' && (action.startsWith('http') || action.startsWith('/'))) {
127
- return action;
128
- }
129
-
130
- return null;
131
- }
132
-
133
- export { ButtonHoverColor, PresetButtonActions };
134
-
135
- interface MenuButtonProps {
136
- onClick?: () => void;
137
- isOpen?: boolean;
138
- strings?: any;
139
- }
140
-
141
- export function MenuButton({ onClick, isOpen = false, strings }: MenuButtonProps) {
142
- return (
143
- <button className={`button button-menu ${isOpen ? 'open' : ''}`} onClick={onClick}>
144
- <span className="menu-text">{strings.common.menu}</span>
145
- <div className="menu-icon">
146
- <span></span>
147
- <span></span>
148
- <span></span>
149
- </div>
150
- </button>
151
- );
152
- }