@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,122 +0,0 @@
1
- .productTile {
2
- height: 100%;
3
- width: 100%;
4
- text-decoration: none;
5
- position: relative;
6
- margin-top: var(--spacing-large);
7
- }
8
-
9
- .card {
10
- width: 100%;
11
- height: 380px;
12
- perspective: 1000px;
13
- cursor: pointer;
14
- }
15
-
16
- .cardInner {
17
- position: relative;
18
- width: 100%;
19
- height: 100%;
20
- transition: transform 0.6s;
21
- transform-style: preserve-3d;
22
- }
23
-
24
- .card.isFlipped .cardInner {
25
- transform: rotateY(180deg);
26
- }
27
-
28
- .cardFaceFront,
29
- .cardFaceBack {
30
- position: absolute;
31
- width: 100%;
32
- height: 100%;
33
- -webkit-backface-visibility: hidden;
34
- backface-visibility: hidden;
35
- }
36
-
37
- .productTileImageContainer {
38
- width: 100%;
39
- height: 100%;
40
- position: relative;
41
- border-radius: var(--border-radius-small);
42
- overflow: hidden;
43
- border: 10px solid var(--accent-orange);
44
- box-sizing: border-box;
45
- }
46
-
47
- .cardFaceFront {
48
- transform: rotateY(0deg);
49
- }
50
-
51
- .cardFaceBack {
52
- transform: rotateY(180deg);
53
- height: 380px;
54
- background: var(--grey-text-dark-70);
55
- border-radius: var(--border-radius-small);
56
- }
57
-
58
- .productTilePillValuesView {
59
- height: 100%;
60
- display: flex;
61
- flex-direction: column;
62
- align-items: center;
63
- justify-content: center;
64
- gap: 8px;
65
- }
66
-
67
- .productTileImage {
68
- width: 100%;
69
- height: 100%;
70
- position: relative;
71
- }
72
-
73
- .productTileImage::after {
74
- content: '';
75
- position: absolute;
76
- bottom: 0;
77
- left: 0;
78
- right: 0;
79
- height: 30%;
80
- background: linear-gradient(to top, rgba(0,0,0,0.5), transparent);
81
- z-index: 1;
82
- }
83
-
84
- .productTileContentOverlay {
85
- position: absolute;
86
- top: 0;
87
- left: 0;
88
- right: 0;
89
- padding: 8px;
90
- z-index: 2;
91
- display: flex;
92
- flex-direction: column;
93
- }
94
-
95
- .productTileTags {
96
- display: flex;
97
- flex-direction: row;
98
- gap: var(--spacing-small);
99
- }
100
-
101
- .productTileTitle {
102
- margin: 0;
103
- color: white;
104
- text-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
105
- }
106
-
107
- .productStartTime {
108
- position: absolute;
109
- bottom: 0;
110
- right: 0;
111
- color: #FFFFFF;
112
- font-family: 'Northlake', sans-serif;
113
- font-weight: 400;
114
- font-size: 2.5rem;
115
- text-align: right;
116
- z-index: 2;
117
- line-height: 0.8;
118
- }
119
-
120
- .backCardProductTileButton {
121
- margin-top: var(--spacing-small);
122
- }
@@ -1,89 +0,0 @@
1
- 'use client';
2
- import { Product } from "@/constants/products"
3
- import ViaViaImage from "../image"
4
- import styles from "./image-only-product-tile-mobile.module.css"
5
- import { PillVariant } from "../value-pill"
6
- import ValuePill from "../value-pill"
7
- import Button from "../button";
8
- import defaultStrings from "@/strings";
9
- import ProductTag from "../product-tag";
10
- import { OPEN_BOOKING_FOR_PRODUCT } from "@/providers/booking-dialog-provider";
11
-
12
- export default function ProductTileMobile({
13
- product,
14
- isFlipped,
15
- onFlip,
16
- strings = defaultStrings
17
- }: {
18
- product: Product;
19
- isFlipped: boolean;
20
- onFlip: () => void;
21
- strings?: any;
22
- }) {
23
- const handleClick = (e: React.MouseEvent) => {
24
- e.preventDefault();
25
- onFlip();
26
- };
27
-
28
- const handleMoreInfo = (e: React.MouseEvent) => {
29
- e.preventDefault();
30
- e.stopPropagation();
31
- window.dispatchEvent(
32
- new CustomEvent(OPEN_BOOKING_FOR_PRODUCT, { detail: { productId: product.id } })
33
- );
34
- };
35
-
36
- return (
37
- <div className={styles.productTile} onClick={handleClick}>
38
- <div className={`${styles.card} ${isFlipped ? styles.isFlipped : ''}`}>
39
- <div className={styles.cardInner}>
40
- <div className={styles.cardFaceFront}>
41
- <div className={styles.productTileImageContainer}>
42
- <ViaViaImage
43
- className={styles.productTileImage}
44
- imageId={product.images[0].id}
45
- alt={product.images[0].alt}
46
- context="GALLERY"
47
- />
48
- <div className={styles.productTileContentOverlay}>
49
- {/* Render tags if they exist */}
50
- {product.tags && (
51
- <div className={styles.productTileTags}>
52
- {product.tags.map((tag, index) => (
53
- <ProductTag
54
- key={`${tag.text}-${index}`}
55
- text={tag.text}
56
- style={tag.style}
57
- />
58
- ))}
59
- </div>
60
- )}
61
- <h3 className={styles.productTileTitle}>{product.shortName}</h3>
62
- </div>
63
- <span className={styles.productStartTime} dangerouslySetInnerHTML={{ __html: product.currentStartTime }} />
64
- </div>
65
- </div>
66
- <div className={styles.cardFaceBack}>
67
- <div className={styles.productTilePillValuesView}>
68
- {product.pillValues.map((pillValue, index) => (
69
- <ValuePill
70
- key={`${pillValue.label}-${index}`}
71
- variant={PillVariant.solid}
72
- pillValue={pillValue}
73
- />
74
- ))}
75
- <Button
76
- variant="outline"
77
- className={styles.backCardProductTileButton}
78
- action=""
79
- onClick={handleMoreInfo}
80
- >
81
- {strings.common.moreInfo}
82
- </Button>
83
- </div>
84
- </div>
85
- </div>
86
- </div>
87
- </div>
88
- );
89
- }
@@ -1,44 +0,0 @@
1
- 'use client';
2
- import { Product } from "@/constants/products"
3
- import { useState, useEffect } from "react";
4
- import ProductTileDesktop from "./image-only-product-tile-desktop";
5
- import ProductTileMobile from "./image-only-product-tile-mobile";
6
-
7
- export default function ProductTile({
8
- product,
9
- flippedProductId,
10
- onFlip,
11
- strings
12
- }: {
13
- product: Product;
14
- flippedProductId?: string;
15
- onFlip?: (productId: string) => void;
16
- strings?: any;
17
- }) {
18
- const [isMobile, setIsMobile] = useState(false); // Default to desktop view
19
- const [hasMounted, setHasMounted] = useState(false);
20
-
21
- useEffect(() => {
22
- setHasMounted(true);
23
- const checkMobile = () => setIsMobile(window.innerWidth < 1025);
24
- checkMobile();
25
- window.addEventListener('resize', checkMobile);
26
- return () => window.removeEventListener('resize', checkMobile);
27
- }, []);
28
-
29
- // Always render desktop version for SEO/initial render
30
- if (!hasMounted) {
31
- return <ProductTileDesktop product={product} strings={strings} />;
32
- }
33
-
34
- return isMobile ? (
35
- <ProductTileMobile
36
- product={product}
37
- isFlipped={flippedProductId === product.id}
38
- onFlip={() => onFlip?.(product.id)}
39
- strings={strings}
40
- />
41
- ) : (
42
- <ProductTileDesktop product={product} strings={strings} />
43
- );
44
- }
@@ -1,84 +0,0 @@
1
- .bestOptionCard {
2
- background-color: var(--accent-white);
3
- border-radius: 1rem;
4
- box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
5
- overflow: hidden;
6
- width: 100%;
7
- display: flex;
8
- flex-direction: column;
9
- height: 100%;
10
- color: inherit;
11
- }
12
-
13
- .bestOptionCardImage img { /* Target the actual image element */
14
- transition: transform 0.3s ease-in-out;
15
- will-change: transform; /* Optimize performance */
16
- }
17
-
18
- .bestOptionCardImageContainer {
19
- position: relative;
20
- display: flex;
21
- min-height: 250px; /* Minimum height for short content */
22
- }
23
-
24
- .bestOptionCardImage {
25
- width: 100%;
26
- height: 100%; /* Fill container height */
27
- object-fit: cover;
28
- position: absolute; /* Position behind pills */
29
- top: 0;
30
- left: 0;
31
- overflow: hidden;
32
- }
33
-
34
- .bestOptionCardTitle {
35
- font-weight: 700;
36
- font-size: 1.2rem;
37
- text-align: left;
38
- width: 100%;
39
- margin-bottom: 1rem;
40
- word-wrap: break-word;
41
- overflow-wrap: break-word;
42
- max-width: 100%;
43
- text-decoration: none;
44
- }
45
-
46
- .bestOptionCardContent {
47
- display: flex;
48
- flex-direction: column;
49
- align-items: center;
50
- width: 100%;
51
- padding: 1rem;
52
- flex: 1;
53
- justify-content: space-between;
54
- box-sizing: border-box;
55
- }
56
-
57
- .bestOptionCardButton {
58
- width: fit-content;
59
- margin-top: auto;
60
- }
61
-
62
- .productTilePillValuesView {
63
- position: relative;
64
- width: 100%;
65
- display: flex;
66
- flex-direction: row;
67
- flex-wrap: wrap;
68
- gap: 4px;
69
- align-content: flex-end;
70
- justify-content: flex-start;
71
- padding: 16px;
72
- z-index: 1;
73
- }
74
-
75
- /* Add hover effect for desktop */
76
- @media (min-width: 1024px) {
77
- .bestOptionCard:hover .productTilePillValuesView {
78
- opacity: 1;
79
- }
80
-
81
- .bestOptionCard:hover .bestOptionCardImage img {
82
- transform: scale(1.1); /* 10% zoom on hover */
83
- }
84
- }
@@ -1,61 +0,0 @@
1
- "use client";
2
-
3
- import { Product } from "@/constants/products";
4
- import ViaViaImage from "../image";
5
- import styles from "./product-tile-card.module.css";
6
- import Button, { ButtonHoverColor } from "../button";
7
- import defaultStrings from "@/strings";
8
- import ValuePill from "../value-pill";
9
- import { PillVariant } from "../value-pill";
10
- import { OPEN_BOOKING_FOR_PRODUCT } from "@/providers/booking-dialog-provider";
11
-
12
- export default function ProductTileCard({ product, strings = defaultStrings }: { product: Product; strings?: any }) {
13
- const handleClick = (e: React.MouseEvent) => {
14
- e.preventDefault();
15
- window.dispatchEvent(
16
- new CustomEvent(OPEN_BOOKING_FOR_PRODUCT, { detail: { productId: product.id } })
17
- );
18
- };
19
-
20
- return (
21
- <div onClick={handleClick} className={styles.bestOptionCard} style={{ cursor: 'pointer' }}>
22
- <div className={styles.bestOptionCardImageContainer}>
23
- <ViaViaImage
24
- imageId={product.images[0].id}
25
- alt={product.images[0].alt}
26
- context="GALLERY"
27
- className={styles.bestOptionCardImage}
28
- />
29
- <ProductTilePillValuesView product={product} />
30
- </div>
31
- <div className={styles.bestOptionCardContent}>
32
- <p className={styles.bestOptionCardTitle}>{product.name}</p>
33
- <Button
34
- variant="primary"
35
- className={styles.bestOptionCardButton}
36
- hoverColor={ButtonHoverColor.Turquoise}
37
- >
38
- {strings.common.moreInfo}
39
- </Button>
40
- </div>
41
- </div>
42
- );
43
- }
44
-
45
- function ProductTilePillValuesView({
46
- product
47
- }: {
48
- product: Product
49
- }) {
50
- return (
51
- <div className={styles.productTilePillValuesView}>
52
- {product.pillValues.map((pillValue, index) => (
53
- <ValuePill
54
- key={index}
55
- variant={PillVariant.overlay}
56
- pillValue={pillValue}
57
- />
58
- ))}
59
- </div>
60
- )
61
- }
@@ -1,85 +0,0 @@
1
- .review-highlights-section {
2
- background-color: var(--accent-turquoise-60);
3
- padding: var(--spacing-large) 0;
4
- }
5
-
6
- .review-highlights-container {
7
- overflow-x: auto;
8
- -ms-overflow-style: none;
9
- scrollbar-width: none;
10
- }
11
-
12
- .review-highlights-container::-webkit-scrollbar {
13
- display: none;
14
- }
15
-
16
- .review-highlights-row {
17
- display: flex;
18
- gap: 24px;
19
- min-width: min-content;
20
- }
21
-
22
- .review-highlights-title {
23
- color: var(--accent-white);
24
- padding: 0 var(--spacing-small);
25
- }
26
-
27
- .review-card {
28
- min-width: 300px;
29
- background: white;
30
- padding: 32px;
31
- border-radius: 40px;
32
- position: relative;
33
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
34
- margin-top: 50px;
35
- margin-right: 40px;
36
- display: flex;
37
- flex-direction: column;
38
- justify-content: space-between;
39
- }
40
-
41
- .author-avatar {
42
- width: 80px;
43
- height: 80px;
44
- border-radius: 50%;
45
- background: transparent;
46
- position: absolute;
47
- top: -40px;
48
- right: -40px;
49
- border: 4px solid white;
50
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
51
- }
52
-
53
- .review-author-name {
54
- text-align: right;
55
- padding-top: var(--spacing-small);
56
- }
57
-
58
- /* Desktop */
59
- @media (min-width: 1024px) {
60
- .review-quote {
61
- font-size: 14px;
62
- }
63
- }
64
-
65
- .expand-button {
66
- background: none;
67
- border: none;
68
- color: var(--accent-orange);
69
- cursor: pointer;
70
- font-weight: 600;
71
- font-family: 'Figtree', sans-serif;
72
- padding: 0;
73
- margin-left: 4px;
74
- text-decoration: underline;
75
- transition: color 0.2s ease;
76
- }
77
-
78
- .expand-button:hover {
79
- color: var(--accent-orange-dark);
80
- }
81
-
82
- .expand-button:focus {
83
- outline: none;
84
- color: var(--accent-orange-dark);
85
- }
@@ -1,127 +0,0 @@
1
- "use client";
2
-
3
- import { Parallax } from 'react-scroll-parallax';
4
- import './review-highlights-section.css';
5
- import ParallaxWrapper from '@/components/parallax-provider';
6
- import { REVIEWS, Review } from '@/constants/reviews';
7
- import Image from 'next/image';
8
- import StarRating from '@/components/star-rating';
9
- import { useState } from 'react';
10
- import TranslatableReviews from './translatable-reviews';
11
- import { usePathname } from 'next/navigation';
12
-
13
- function ReviewCard({
14
- review,
15
- originalReview,
16
- showOriginal,
17
- onToggleOriginal,
18
- language
19
- }: {
20
- review: typeof REVIEWS[0];
21
- originalReview?: typeof REVIEWS[0];
22
- showOriginal?: boolean;
23
- onToggleOriginal?: () => void;
24
- language: 'en' | 'es' | 'fr';
25
- }) {
26
- const [isExpanded, setIsExpanded] = useState(false);
27
-
28
- // Use original review if showing original, otherwise use the current review
29
- const displayReview = showOriginal && originalReview ? originalReview : review;
30
-
31
- // Truncate text to first 100 characters
32
- const truncatedText = displayReview.review.length > 500 ? displayReview.review.substring(0, 500) + '...' : displayReview.review;
33
- const shouldShowExpand = displayReview.review.length > 500;
34
-
35
- return (
36
- <div className="review-card">
37
- <p className="review-quote">
38
- <span dangerouslySetInnerHTML={{
39
- __html: isExpanded ? displayReview.review : truncatedText
40
- }} />
41
- {shouldShowExpand && (
42
- <button
43
- className="expand-button"
44
- onClick={() => setIsExpanded(!isExpanded)}
45
- >
46
- {isExpanded ? ' Show less' : ' Read more'}
47
- </button>
48
- )}
49
- </p>
50
- {(language === 'es' || language === 'fr') && originalReview && onToggleOriginal && (
51
- <div style={{ marginBottom: '1rem', textAlign: 'center' }}>
52
- <button
53
- onClick={onToggleOriginal}
54
- style={{
55
- background: 'none',
56
- border: 'none',
57
- color: 'var(--accent-orange)',
58
- cursor: 'pointer',
59
- fontWeight: '600',
60
- fontFamily: "'Figtree', sans-serif",
61
- padding: '0',
62
- textDecoration: 'underline',
63
- transition: 'color 0.2s ease'
64
- }}
65
- >
66
- {showOriginal ? (language === 'es' ? 'Ver traducido' : 'Voir traduit') : (language === 'es' ? 'Ver original' : 'Voir original')}
67
- </button>
68
- </div>
69
- )}
70
- <div className="review-author">
71
- <div className="author-avatar">
72
- <Image src={displayReview.profilePicture} alt={displayReview.author} width={80} height={80} />
73
- </div>
74
- </div>
75
- <div>
76
- <p className="review-author-name">{displayReview.author}</p>
77
- <StarRating rating={5} align="right" />
78
- </div>
79
- </div>
80
- );
81
- }
82
-
83
- export default function ReviewHighlightsSection({ className, strings }: { className: string; strings: any }) {
84
- const pathname = usePathname();
85
- const language = pathname?.startsWith('/es') ? 'es' : pathname?.startsWith('/fr') ? 'fr' : 'en';
86
- const [individualToggles, setIndividualToggles] = useState<Record<string, boolean>>({});
87
-
88
- const toggleIndividualOriginal = (reviewLink: string) => {
89
- setIndividualToggles(prev => ({
90
- ...prev,
91
- [reviewLink]: !prev[reviewLink]
92
- }));
93
- };
94
-
95
- return (
96
- <div id="reviews-section" className={`review-highlights-section ${className}`}>
97
- <ParallaxWrapper>
98
- <h2 className="review-highlights-title">{strings.home.reviewHighlights.title}</h2>
99
- <section className="review-highlights-container">
100
- <Parallax translateX={['100%', '-100%']}>
101
- <div className="review-highlights-row">
102
- <TranslatableReviews language={language}>
103
- {(reviews) =>
104
- reviews.map((review, index) => {
105
- const originalReview = REVIEWS[index];
106
- const showOriginal = individualToggles[review.link] || false;
107
-
108
- return (
109
- <ReviewCard
110
- key={review.link}
111
- review={review}
112
- originalReview={originalReview}
113
- showOriginal={showOriginal}
114
- onToggleOriginal={() => toggleIndividualOriginal(review.link)}
115
- language={language}
116
- />
117
- );
118
- })
119
- }
120
- </TranslatableReviews>
121
- </div>
122
- </Parallax>
123
- </section>
124
- </ParallaxWrapper>
125
- </div>
126
- );
127
- }