@ticketboothapp/booking 0.1.19 → 0.1.22

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 (106) hide show
  1. package/package.json +2 -1
  2. package/src/components/BookingWidget.tsx +282 -26
  3. package/src/components/ManageBookingView.tsx +75 -23
  4. package/src/components/PostBookingDependentAddOnUpsell.tsx +1 -1
  5. package/src/components/booking/BookingProductGrid.tsx +1 -1
  6. package/src/components/booking/Calendar.module.css +3 -3
  7. package/src/components/booking/CheckoutForm.tsx +1 -1
  8. package/src/components/booking/InfoTooltip.tsx +2 -13
  9. package/src/components/booking/PickupLocationSelector.tsx +2 -2
  10. package/src/components/booking/PriceBreakdown.tsx +11 -34
  11. package/src/index.ts +3 -1
  12. package/tsconfig.json +1 -1
  13. package/src/components/JobApplicationDialog.module.css +0 -440
  14. package/src/components/JobApplicationDialog.tsx +0 -620
  15. package/src/components/PickupLocationMap.tsx +0 -110
  16. package/src/components/accordion.css +0 -27
  17. package/src/components/accordion.tsx +0 -29
  18. package/src/components/analytics/AnalyticsConsentRestore.tsx +0 -19
  19. package/src/components/analytics/AnalyticsScripts.tsx +0 -106
  20. package/src/components/analytics/CookieConsentBanner.css +0 -86
  21. package/src/components/analytics/CookieConsentBanner.tsx +0 -102
  22. package/src/components/bottom-sheet.module.css +0 -78
  23. package/src/components/bottom-sheet.tsx +0 -60
  24. package/src/components/breadcrumb.module.css +0 -40
  25. package/src/components/breadcrumb.tsx +0 -36
  26. package/src/components/client-bottom-sheet.tsx +0 -14
  27. package/src/components/conditional-footer.tsx +0 -27
  28. package/src/components/contact-us.module.css +0 -147
  29. package/src/components/contact-us.tsx +0 -49
  30. package/src/components/email-signup.css +0 -151
  31. package/src/components/email-signup.tsx +0 -63
  32. package/src/components/faq-wrapper.module.css +0 -47
  33. package/src/components/faq-wrapper.tsx +0 -15
  34. package/src/components/footer.css +0 -187
  35. package/src/components/footer.tsx +0 -143
  36. package/src/components/global-simple-modal.tsx +0 -33
  37. package/src/components/google-review-summary.module.css +0 -77
  38. package/src/components/google-review-summary.tsx +0 -50
  39. package/src/components/hero-image.css +0 -13
  40. package/src/components/hero-image.tsx +0 -44
  41. package/src/components/language-aware-link.tsx +0 -72
  42. package/src/components/language-switcher.module.css +0 -124
  43. package/src/components/language-switcher.tsx +0 -75
  44. package/src/components/map-section.css +0 -59
  45. package/src/components/map-section.tsx +0 -63
  46. package/src/components/navbar.module.css +0 -152
  47. package/src/components/navbar.tsx +0 -125
  48. package/src/components/parallax-provider.tsx +0 -11
  49. package/src/components/product-theme-pages/best-option.module.css +0 -70
  50. package/src/components/product-theme-pages/best-option.tsx +0 -35
  51. package/src/components/product-theme-pages/extended-tour-options.module.css +0 -22
  52. package/src/components/product-theme-pages/extended-tour-options.tsx +0 -11
  53. package/src/components/product-theme-pages/photo-gallery.tsx +0 -90
  54. package/src/components/product-theme-pages/product-theme-page-layout.module.css +0 -13
  55. package/src/components/product-theme-pages/product-theme-page-layout.tsx +0 -67
  56. package/src/components/product-theme-pages/top-of-fold.module.css +0 -179
  57. package/src/components/product-theme-pages/top-of-fold.tsx +0 -80
  58. package/src/components/product-tile/image-only-product-tile-desktop.module.css +0 -106
  59. package/src/components/product-tile/image-only-product-tile-desktop.tsx +0 -56
  60. package/src/components/product-tile/image-only-product-tile-mobile.module.css +0 -122
  61. package/src/components/product-tile/image-only-product-tile-mobile.tsx +0 -89
  62. package/src/components/product-tile/image-only-product-tile.tsx +0 -44
  63. package/src/components/product-tile/product-tile-card.module.css +0 -84
  64. package/src/components/product-tile/product-tile-card.tsx +0 -61
  65. package/src/components/review-highlights-section.css +0 -85
  66. package/src/components/review-highlights-section.tsx +0 -127
  67. package/src/components/season-closure-overlay.module.css +0 -99
  68. package/src/components/season-closure-overlay.tsx +0 -98
  69. package/src/components/simple-modal.tsx +0 -69
  70. package/src/components/simple-top-of-fold.module.css +0 -76
  71. package/src/components/simple-top-of-fold.tsx +0 -34
  72. package/src/components/spacer.css +0 -41
  73. package/src/components/spacer.tsx +0 -23
  74. package/src/components/star-rating.module.css +0 -74
  75. package/src/components/star-rating.tsx +0 -48
  76. package/src/components/title-subtitle.module.css +0 -10
  77. package/src/components/title-subtitle.tsx +0 -30
  78. package/src/components/translatable-reviews.tsx +0 -75
  79. package/src/components/value-props.css +0 -185
  80. package/src/components/value-props.tsx +0 -88
  81. package/src/constants/booking-guide-quiz.ts +0 -64
  82. package/src/constants/contact-info.ts +0 -2
  83. package/src/constants/faq.ts +0 -44
  84. package/src/constants/json-ld/faq-json-ld.tsx +0 -170
  85. package/src/constants/json-ld/homepage-json-ld.tsx +0 -138
  86. package/src/constants/json-ld/job-posting-json-ld.tsx +0 -92
  87. package/src/constants/json-ld/organization-json-ld.tsx +0 -62
  88. package/src/constants/json-ld/page-json-ld.tsx +0 -6
  89. package/src/constants/json-ld/product-json-ld.tsx +0 -154
  90. package/src/constants/json-ld/review-json-ld.tsx +0 -377
  91. package/src/constants/navigation-links/footer-links.ts +0 -48
  92. package/src/constants/navigation-links/nav-bar-links.ts +0 -41
  93. package/src/constants/navigation-links/navigation-link.ts +0 -6
  94. package/src/constants/quiz-recommendations.ts +0 -506
  95. package/src/constants/reviews.ts +0 -75
  96. package/src/constants/staff.ts +0 -197
  97. package/src/constants/value-props.ts +0 -58
  98. package/src/hooks/use-bottom-sheet.tsx +0 -15
  99. package/src/hooks/use-simple-modal.tsx +0 -27
  100. package/src/hooks/useEmailSubscription.tsx +0 -103
  101. package/src/hooks/useEmbeddedInIframe.ts +0 -16
  102. package/src/hooks/useQuiz.tsx +0 -210
  103. package/src/providers/bottom-sheet-provider.tsx +0 -40
  104. package/src/types/fareharbor.d.ts +0 -12
  105. package/src/types/quiz.ts +0 -59
  106. /package/src/{app/photo-sessions → lib}/photo-packages.ts +0 -0
@@ -1,143 +0,0 @@
1
- 'use client';
2
-
3
- import './footer.css';
4
- import { getStrings } from '@/strings';
5
- import FacebookIcon from '@/assets/icons/facebook.svg';
6
- import InstagramIcon from '@/assets/icons/instagram.svg';
7
- import TikTokIcon from '@/assets/icons/tiktok.svg';
8
- import GoogleIcon from '@/assets/icons/google-g-white.svg';
9
- import Spacer from './spacer';
10
- import Button, { ButtonHoverColor } from './button';
11
- import { getFooterLinks } from '@/constants/navigation-links/footer-links';
12
- import VIcon from '@/assets/icons/viavia-v.svg';
13
- import IIcon from '@/assets/icons/viavia-i.svg';
14
- import AIcon from '@/assets/icons/viavia-a.svg';
15
- import Link from 'next/link';
16
- import LanguageAwareLink from './language-aware-link';
17
- import { useBottomSheet } from '@/providers/bottom-sheet-provider';
18
- import ContactUs from './contact-us';
19
- import { useEffect, Suspense } from 'react';
20
- import { usePathname, useSearchParams } from 'next/navigation';
21
- import MoraineMagicIcon from '@/assets/moraine-magic-white.svg';
22
- import EmailSignup from './email-signup';
23
-
24
- function ContactHandler() {
25
- const { openSheet } = useBottomSheet();
26
- const pathname = usePathname();
27
- const searchParams = useSearchParams();
28
-
29
- useEffect(() => {
30
- if (window.location.hash === '#contact') {
31
- // Detect language from pathname inside the effect
32
- const language = pathname?.startsWith('/es') ? 'es' : pathname?.startsWith('/fr') ? 'fr' : 'en';
33
- const strings = getStrings(language);
34
-
35
- // Prevent default scroll behavior
36
- const handleHashChange = (e: HashChangeEvent) => {
37
- if (e.newURL.includes('#contact')) {
38
- e.preventDefault();
39
- }
40
- };
41
-
42
- window.addEventListener('hashchange', handleHashChange);
43
- openSheet(<ContactUs strings={strings} />);
44
- // Removed the history.replaceState line to keep the hash in URL
45
-
46
- return () => {
47
- window.removeEventListener('hashchange', handleHashChange);
48
- };
49
- }
50
- }, [pathname, searchParams, openSheet]);
51
-
52
- return null;
53
- }
54
-
55
- export default function Footer() {
56
- const { openSheet } = useBottomSheet();
57
- const pathname = usePathname();
58
-
59
- // Detect language from pathname
60
- const language = pathname?.startsWith('/es') ? 'es' : pathname?.startsWith('/fr') ? 'fr' : 'en';
61
- const strings = getStrings(language);
62
-
63
- const handleContactClick = () => {
64
- openSheet(<ContactUs strings={strings} />);
65
- };
66
-
67
- return (
68
- <div className={`footer-container`}>
69
- <Suspense>
70
- <ContactHandler />
71
- </Suspense>
72
- <LanguageAwareLink href="/" className="footer-logo-container">
73
- <div className="footer-via-via-name">
74
- <VIcon className="footer-logo-letter" />
75
- <IIcon className="footer-logo-letter" />
76
- <AIcon className="footer-logo-letter" />
77
- </div>
78
- <Spacer horizontal = {true} />
79
- <div className="footer-via-via-name">
80
- <VIcon className="footer-logo-letter" />
81
- <IIcon className="footer-logo-letter" />
82
- <AIcon className="footer-logo-letter" />
83
- </div>
84
- </LanguageAwareLink>
85
- <Spacer />
86
- <div style={{ display: 'flex', gap: '1rem', marginBottom: '1rem' }}>
87
- <a href="https://instagram.com/viaviamorainelake" target="_blank" rel="noopener noreferrer">
88
- <InstagramIcon className="footer-icon" />
89
- </a>
90
- <a href="https://www.facebook.com/profile.php?id=61557238871790" target="_blank" rel="noopener noreferrer">
91
- <FacebookIcon className="footer-icon" />
92
- </a>
93
- <a href="https://www.tiktok.com/@viaviamorainelake" target="_blank" rel="noopener noreferrer">
94
- <TikTokIcon className="footer-icon" />
95
- </a>
96
- <a href="https://maps.app.goo.gl/FxRqxTjgN43ttWac9" target="_blank" rel="noopener noreferrer">
97
- <GoogleIcon className="footer-icon" />
98
- </a>
99
- </div>
100
- <Spacer />
101
- <FooterLinks strings={strings} />
102
- <Spacer size='L' />
103
- <Button
104
- onClick={handleContactClick}
105
- variant="secondary"
106
- >
107
- {strings.footer.contactUs}
108
- </Button>
109
- <Spacer />
110
- <p onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })} style={{ cursor: 'pointer' }}>
111
- ↑ <a><u>{strings.footer.returnToTop}</u></a> ↑
112
- </p>
113
- <Spacer />
114
- <MoraineMagicIcon className="footer-moraine-magic-icon" />
115
- <Spacer />
116
- <EmailSignup strings={strings} source="footer" />
117
- <Spacer />
118
- <p><LanguageAwareLink href="/terms-conditions">{strings.footer["terms-and-conditions"]}</LanguageAwareLink></p>
119
- <p><LanguageAwareLink href="/privacy-policy">{strings.footer["privacy-policy"]}</LanguageAwareLink></p>
120
- <p><LanguageAwareLink href="/sitemap.xml">{strings.footer["sitemap"]}</LanguageAwareLink></p>
121
- <Spacer />
122
- <p>{strings.footer.copyright}</p>
123
- <p className="footer-company-text" style={{ textTransform: 'uppercase', textAlign: 'center' }}>{strings.footer.company}</p>
124
- </div>
125
- )
126
- }
127
-
128
- function FooterLinks({ strings }: { strings: any }) {
129
- const footerLinks = getFooterLinks(strings);
130
- return (
131
- <div className="footer-links-container">
132
- {Object.values(footerLinks).map((link) => (
133
- link.external ? (
134
- <a key={link.name} href={link.url} target="_blank" rel="noopener noreferrer">
135
- {link.name}
136
- </a>
137
- ) : (
138
- <LanguageAwareLink key={link.name} href={link.path ?? link.url ?? '/'}>{link.name}</LanguageAwareLink>
139
- )
140
- ))}
141
- </div>
142
- )
143
- }
@@ -1,33 +0,0 @@
1
- "use client";
2
-
3
- import { useSimpleModal } from '@/hooks/use-simple-modal';
4
- import SimpleModal from './simple-modal';
5
- import { useEffect } from 'react';
6
-
7
- export default function GlobalSimpleModal() {
8
- const { isModalOpen, closeModal } = useSimpleModal();
9
-
10
- useEffect(() => {
11
- const handleBookNowClick = (e: Event) => {
12
- const target = e.target as HTMLElement;
13
- const link = target.closest('a[href="#book-now"]');
14
- if (link) {
15
- e.preventDefault();
16
- window.dispatchEvent(new CustomEvent('openSimpleModal'));
17
- }
18
- };
19
-
20
- document.addEventListener('click', handleBookNowClick);
21
-
22
- return () => {
23
- document.removeEventListener('click', handleBookNowClick);
24
- };
25
- }, []);
26
-
27
- return (
28
- <SimpleModal
29
- isOpen={isModalOpen}
30
- onClose={closeModal}
31
- />
32
- );
33
- }
@@ -1,77 +0,0 @@
1
- .googleReviewSummary {
2
- display: flex;
3
- align-items: center;
4
- flex-direction: column;
5
- padding: 1rem;
6
- transition: transform 0.2s ease, filter 0.2s ease;
7
- cursor: pointer;
8
- }
9
-
10
- .googleReviewSummary:hover {
11
- transform: scale(1.05);
12
- filter: brightness(1.1);
13
- }
14
-
15
- .googleReviewSummary:active {
16
- transform: scale(1.02);
17
- }
18
-
19
- .centerSection {
20
- display: flex;
21
- flex-direction: column;
22
- align-items: center;
23
- position: relative;
24
- }
25
-
26
- .logoRow {
27
- display: flex;
28
- align-items: center;
29
- justify-content: center;
30
- gap: 0;
31
- margin-bottom: 0.5rem;
32
- flex-direction: row;
33
- width: 100%;
34
- position: relative;
35
- }
36
-
37
- .sideLogo {
38
- filter: brightness(0) invert(1);
39
- position: absolute;
40
- z-index: 0;
41
- opacity: 0.7;
42
- flex-shrink: 0;
43
- top: 50%;
44
- transform: translateY(5%);
45
- }
46
-
47
- .sideLogo:first-child {
48
- left: 5%;
49
- }
50
-
51
- .sideLogo:last-child {
52
- right: 5%;
53
- }
54
-
55
- .googleIcon {
56
- --google-blue: var(--accent-white);
57
- --google-red: var(--accent-white);
58
- --google-yellow: var(--accent-white);
59
- --google-green: var(--accent-white);
60
- --google-lightblue: var(--accent-white);
61
- filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.5));
62
- position: relative;
63
- z-index: 2;
64
- margin-bottom: -2.5px;
65
- }
66
-
67
- .googleReviewsCount {
68
- font-size: 16px;
69
- color: var(--accent-white);
70
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
71
- font-family: 'Poppins', sans-serif;
72
- }
73
-
74
- .googleReviewSummary :global(.starRating) {
75
- position: relative;
76
- z-index: 1;
77
- }
@@ -1,50 +0,0 @@
1
- import GoogleIcon from '@/assets/icons/google-g.svg';
2
- import TripAdvisorIcon from '@/assets/icons/partner-logos/tripadvisor.svg';
3
- import GetYourGuideIcon from '@/assets/icons/partner-logos/getyourguide.svg';
4
- import styles from './google-review-summary.module.css';
5
- import StarRating from './star-rating';
6
-
7
- export default function GoogleReviewSummary({ strings }: { strings: any }) {
8
- const scrollToReviews = () => {
9
- const reviewsSection = document.getElementById('reviews-section');
10
- if (reviewsSection) {
11
- const elementTop = reviewsSection.offsetTop;
12
- const offset = 100; // Adjust this value to control how high the section appears
13
- window.scrollTo({
14
- top: elementTop - offset,
15
- behavior: 'smooth'
16
- });
17
- }
18
- };
19
-
20
- return (
21
- <div
22
- className={styles.googleReviewSummary}
23
- onClick={scrollToReviews}
24
- style={{ cursor: 'pointer' }}
25
- title="Click to see all reviews"
26
- >
27
- <div className={styles.centerSection}>
28
- <div className={styles.logoRow}>
29
- <TripAdvisorIcon
30
- width={28}
31
- height={28}
32
- className={styles.sideLogo}
33
- />
34
- <GoogleIcon
35
- width={42}
36
- height={42}
37
- className={styles.googleIcon}
38
- />
39
- <GetYourGuideIcon
40
- width={28}
41
- height={28}
42
- className={styles.sideLogo}
43
- />
44
- </div>
45
- <StarRating rating={5} showVerified={false} useGradientSize={true} />
46
- </div>
47
- <p className={styles.googleReviewsCount}>{strings.home.hero.googleReviewsCount}</p>
48
- </div>
49
- );
50
- }
@@ -1,13 +0,0 @@
1
- .hero-image-container {
2
- width: 100%;
3
- height: 100%;
4
- position: relative;
5
- overflow: hidden;
6
- }
7
-
8
- .parallax-container {
9
- width: 100%;
10
- height: 100%;
11
- will-change: transform;
12
- transform: translate3d(0, 0, 0);
13
- }
@@ -1,44 +0,0 @@
1
- "use client";
2
-
3
- import ViaViaImage from './image';
4
- import './hero-image.css';
5
- import { useEffect, useRef } from 'react';
6
-
7
- interface HeroImageProps {
8
- imageId: string;
9
- alt: string;
10
- objectPosition?: string;
11
- }
12
-
13
- const HeroImage = ({ imageId, alt, objectPosition }: HeroImageProps) => {
14
- const containerRef = useRef<HTMLDivElement>(null);
15
-
16
- useEffect(() => {
17
- const container = containerRef.current;
18
- if (!container) return;
19
-
20
- const handleScroll = () => {
21
- const scrolled = Math.max(0, Math.min(window.scrollY, container.offsetHeight));
22
- const rate = scrolled * 0.5;
23
- container.style.transform = `translate3d(0, ${rate}px, 0)`;
24
- };
25
-
26
- window.addEventListener('scroll', handleScroll, { passive: true });
27
- return () => window.removeEventListener('scroll', handleScroll);
28
- }, []);
29
-
30
- return (
31
- <div className="hero-image-container">
32
- <div className="parallax-container" ref={containerRef}>
33
- <ViaViaImage
34
- imageId={imageId}
35
- context="HERO"
36
- alt={alt}
37
- style={objectPosition ? { objectPosition } : undefined}
38
- />
39
- </div>
40
- </div>
41
- );
42
- };
43
-
44
- export default HeroImage;
@@ -1,72 +0,0 @@
1
- 'use client';
2
-
3
- import Link from 'next/link';
4
- import { usePathname } from 'next/navigation';
5
-
6
- interface LanguageAwareLinkProps {
7
- href: string;
8
- children: React.ReactNode;
9
- className?: string;
10
- target?: string;
11
- rel?: string;
12
- onClick?: () => void;
13
- }
14
-
15
- export default function LanguageAwareLink({
16
- href,
17
- children,
18
- className,
19
- target,
20
- rel,
21
- onClick
22
- }: LanguageAwareLinkProps) {
23
- const pathname = usePathname();
24
-
25
- // Check if we're currently on a non-English page
26
- const isSpanishPage = pathname?.startsWith('/es');
27
- const isFrenchPage = pathname?.startsWith('/fr');
28
-
29
- // Define which pages have translated versions (currently only the homepage)
30
- const translatedPages = ['/'];
31
-
32
- const getLocalizedHref = () => {
33
- // If we're on a Spanish page
34
- if (isSpanishPage && href.startsWith('/') && !href.startsWith('/es')) {
35
- // Only prepend /es if this page has a Spanish version
36
- if (translatedPages.includes(href)) {
37
- return `/es${href}`;
38
- }
39
- // Otherwise, go to the English version (remove any /es prefix if present)
40
- return href;
41
- }
42
-
43
- // If we're on a French page
44
- if (isFrenchPage && href.startsWith('/') && !href.startsWith('/fr')) {
45
- // Only prepend /fr if this page has a French version
46
- if (translatedPages.includes(href)) {
47
- return `/fr${href}`;
48
- }
49
- // Otherwise, go to the English version (remove any /fr prefix if present)
50
- return href;
51
- }
52
-
53
- // If we're on an English page, always go to English version
54
- if (!isSpanishPage && !isFrenchPage && (href.startsWith('/es') || href.startsWith('/fr'))) {
55
- return href.replace(/^\/(es|fr)/, '') || '/';
56
- }
57
-
58
- return href;
59
- };
60
-
61
- return (
62
- <Link
63
- href={getLocalizedHref()}
64
- className={className}
65
- target={target}
66
- rel={rel}
67
- onClick={onClick}
68
- >
69
- {children}
70
- </Link>
71
- );
72
- }
@@ -1,124 +0,0 @@
1
- .languageSwitcher {
2
- position: fixed;
3
- bottom: 20px;
4
- left: 20px;
5
- z-index: 1000;
6
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
7
- }
8
-
9
- .currentLanguage {
10
- display: flex;
11
- align-items: center;
12
- gap: 8px;
13
- padding: 8px 12px;
14
- background: rgba(255, 255, 255, 0.95);
15
- border: 1px solid rgba(0, 0, 0, 0.1);
16
- border-radius: var(--border-radius-pill);
17
- cursor: pointer;
18
- transition: all 0.2s ease;
19
- backdrop-filter: blur(10px);
20
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
21
- }
22
-
23
- .currentLanguage:hover {
24
- background: rgba(255, 255, 255, 1);
25
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
26
- transform: translateY(-1px);
27
- }
28
-
29
- .flag {
30
- font-size: 16px;
31
- line-height: 1;
32
- }
33
-
34
- .languageCode {
35
- font-size: 12px;
36
- font-weight: 600;
37
- color: #333;
38
- letter-spacing: 0.5px;
39
- }
40
-
41
- .languageDropdown {
42
- position: absolute;
43
- bottom: 100%;
44
- left: 0;
45
- margin-bottom: 8px;
46
- background: rgba(255, 255, 255, 0.95);
47
- border: 1px solid rgba(0, 0, 0, 0.1);
48
- border-radius: 8px;
49
- backdrop-filter: blur(10px);
50
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
51
- opacity: 0;
52
- visibility: hidden;
53
- transform: translateY(8px);
54
- transition: all 0.2s ease;
55
- min-width: 140px;
56
- }
57
-
58
- .languageDropdown.visible {
59
- opacity: 1;
60
- visibility: visible;
61
- transform: translateY(0);
62
- }
63
-
64
- .languageOption {
65
- display: flex;
66
- align-items: center;
67
- gap: 8px;
68
- padding: 10px 12px;
69
- text-decoration: none;
70
- color: #333;
71
- transition: background-color 0.15s ease;
72
- border-radius: 6px;
73
- margin: 4px;
74
- }
75
-
76
- .languageOption:hover {
77
- background: rgba(0, 0, 0, 0.05);
78
- }
79
-
80
- .languageOption.active {
81
- background: rgba(59, 130, 246, 0.1);
82
- color: #3b82f6;
83
- }
84
-
85
- .languageOption.active .flag {
86
- opacity: 0.8;
87
- }
88
-
89
- .languageName {
90
- font-size: 14px;
91
- font-weight: 500;
92
- }
93
-
94
- /* Mobile responsiveness */
95
- @media (max-width: 768px) {
96
- .languageSwitcher {
97
- bottom: 15px;
98
- left: 15px;
99
- }
100
-
101
- .currentLanguage {
102
- padding: 6px 10px;
103
- }
104
-
105
- .flag {
106
- font-size: 14px;
107
- }
108
-
109
- .languageCode {
110
- font-size: 11px;
111
- }
112
-
113
- .languageDropdown {
114
- min-width: 120px;
115
- }
116
-
117
- .languageOption {
118
- padding: 8px 10px;
119
- }
120
-
121
- .languageName {
122
- font-size: 13px;
123
- }
124
- }
@@ -1,75 +0,0 @@
1
- 'use client';
2
-
3
- import { useState, useEffect } from 'react';
4
- import { usePathname } from 'next/navigation';
5
- import Link from 'next/link';
6
- import styles from './language-switcher.module.css';
7
-
8
- interface Language {
9
- code: string;
10
- name: string;
11
- flag: string;
12
- }
13
-
14
- const languages: Language[] = [
15
- { code: 'en', name: 'english', flag: '🇨🇦' },
16
- { code: 'es', name: 'español', flag: '🇪🇸' },
17
- { code: 'fr', name: 'français', flag: '🇫🇷' },
18
- // Future languages
19
- // { code: 'nl', name: 'Nederlands', flag: '🇳🇱' },
20
- ];
21
-
22
- export default function LanguageSwitcher() {
23
- const [isHovered, setIsHovered] = useState(false);
24
- const pathname = usePathname();
25
-
26
- // Get current language from pathname
27
- const currentLanguage = pathname?.startsWith('/es') ? 'es' :
28
- pathname?.startsWith('/fr') ? 'fr' : 'en';
29
-
30
- const getLanguageUrl = (langCode: string) => {
31
- if (langCode === 'en') {
32
- // Go to English version (remove any language prefix)
33
- return pathname?.replace(/^\/(es|fr)/, '') || '/';
34
- } else if (langCode === 'es') {
35
- // Go to Spanish version (add /es prefix)
36
- const basePath = pathname?.replace(/^\/(es|fr)/, '') || '/';
37
- return `/es${basePath}`;
38
- } else if (langCode === 'fr') {
39
- // Go to French version (add /fr prefix)
40
- const basePath = pathname?.replace(/^\/(es|fr)/, '') || '/';
41
- return `/fr${basePath}`;
42
- }
43
- return '/';
44
- };
45
-
46
- const currentLang = languages.find(lang => lang.code === currentLanguage) || languages[0];
47
-
48
- return (
49
- <div
50
- className={styles.languageSwitcher}
51
- onMouseEnter={() => setIsHovered(true)}
52
- onMouseLeave={() => setIsHovered(false)}
53
- >
54
- <div className={styles.currentLanguage}>
55
- <span className={styles.flag}>{currentLang.flag}</span>
56
- <span className={styles.languageCode}>{currentLang.code.toUpperCase()}</span>
57
- </div>
58
-
59
- <div className={`${styles.languageDropdown} ${isHovered ? styles.visible : ''}`}>
60
- {languages.map((language) => (
61
- <Link
62
- key={language.code}
63
- href={getLanguageUrl(language.code)}
64
- className={`${styles.languageOption} ${
65
- language.code === currentLanguage ? styles.active : ''
66
- }`}
67
- >
68
- <span className={styles.flag}>{language.flag}</span>
69
- <span className={styles.languageName}>{language.name}</span>
70
- </Link>
71
- ))}
72
- </div>
73
- </div>
74
- );
75
- }
@@ -1,59 +0,0 @@
1
- .map-section-wrapper {
2
- width: 100%;
3
- background-color: var(--light-orange-background);
4
- overflow: hidden;
5
- padding-top: var(--spacing-medium);
6
- }
7
-
8
- .map-section-container {
9
- display: flex;
10
- flex-direction: column;
11
- align-items: center;
12
- justify-content: center;
13
- width: fit-content;
14
- min-height: 100px;
15
- padding: 2rem 4rem;
16
- background-color: var(--accent-map-green);
17
- margin: 0 auto;
18
- border-radius: 20px 20px 0px 0px;
19
- }
20
-
21
- .map-section-image {
22
- width: 100%;
23
- min-height: fit-content;
24
- object-fit: fit;
25
- overflow: hidden;
26
- }
27
-
28
- .via-via-map {
29
- width: 100%;
30
- height: auto;
31
- display: block;
32
- }
33
-
34
- @media (max-width: 1023px) {
35
- .map-section-container {
36
- width: 90%;
37
- padding: 2rem 0.5rem; /* Reduce padding on mobile */
38
- }
39
-
40
- .via-via-map {
41
- width: 100%;
42
- }
43
- }
44
-
45
- @media (min-width: 1024px) {
46
- .map-section-container {
47
- width: 70%;
48
- }
49
-
50
- .map-section-image {
51
- width: 100%;
52
- margin-top: 1rem;
53
- }
54
-
55
- .via-via-map {
56
- width: 60%;
57
- margin: 0 auto;
58
- }
59
- }